Port hakuX Phase 2 bindless descriptor indexing

This commit is contained in:
izzy2lost
2026-04-08 02:34:57 -04:00
parent ad2aa65503
commit 0c22bd3a36
9 changed files with 657 additions and 60 deletions
+44 -8
View File
@@ -837,11 +837,19 @@ static MString* psh_convert(struct PixelShader *ps)
ps->state->smooth_shading, true, false, false);
if (ps->opts.vulkan) {
mstring_append_fmt(
preflight,
"layout(location = 0) out vec4 fragColor;\n"
"layout(binding = %d, std140) uniform PshUniforms {\n",
ps->opts.ubo_binding);
if (ps->opts.ubo_set > 0) {
mstring_append_fmt(
preflight,
"layout(location = 0) out vec4 fragColor;\n"
"layout(set = %d, binding = %d, std140) uniform PshUniforms {\n",
ps->opts.ubo_set, ps->opts.ubo_binding);
} else {
mstring_append_fmt(
preflight,
"layout(location = 0) out vec4 fragColor;\n"
"layout(binding = %d, std140) uniform PshUniforms {\n",
ps->opts.ubo_binding);
}
} else {
mstring_append_fmt(preflight,
"layout(location = 0) out vec4 fragColor;\n");
@@ -870,6 +878,18 @@ static MString* psh_convert(struct PixelShader *ps)
mstring_append(preflight, "};\n");
}
if (ps->opts.bindless) {
mstring_append(preflight,
"layout(set = 0, binding = 0) uniform sampler2D texArray2D[1024];\n"
"layout(set = 0, binding = 1) uniform sampler3D texArray3D[1024];\n"
"layout(set = 0, binding = 2) uniform samplerCube texArrayCube[1024];\n");
mstring_append_fmt(preflight,
"layout(push_constant) uniform TexPushData {\n"
" layout(offset = %d) uint texIdx[4];\n"
"};\n",
ps->opts.tex_push_offset);
}
const char *dotmap_funcs[] = {
"dotmap_zero_to_one",
"dotmap_minus1_to_1_d3d",
@@ -1402,10 +1422,26 @@ static MString* psh_convert(struct PixelShader *ps)
}
if (sampler_type != NULL) {
if (ps->opts.vulkan) {
mstring_append_fmt(preflight, "layout(binding = %d) ", ps->opts.tex_binding + i);
if (ps->opts.bindless) {
const char *array_name;
if (strcmp(sampler_type, "sampler3D") == 0) {
array_name = "texArray3D";
} else if (strcmp(sampler_type, "samplerCube") == 0) {
array_name = "texArrayCube";
} else {
array_name = "texArray2D";
}
mstring_append_fmt(preflight,
"#define texSamp%d %s[texIdx[%d]]\n",
i, array_name, i);
} else {
if (ps->opts.vulkan) {
mstring_append_fmt(preflight, "layout(binding = %d) ",
ps->opts.tex_binding + i);
}
mstring_append_fmt(preflight, "uniform %s texSamp%d;\n",
sampler_type, i);
}
mstring_append_fmt(preflight, "uniform %s texSamp%d;\n", sampler_type, i);
/* As this means a texture fetch does happen, do alphakill */
if (ps->state->alphakill[i]) {
+3
View File
@@ -100,7 +100,10 @@ typedef struct GenPshGlslOptions {
bool gles;
int gles_version;
int ubo_binding;
int ubo_set;
int tex_binding;
bool bindless;
int tex_push_offset;
} GenPshGlslOptions;
MString *pgraph_glsl_gen_psh(const PshState *state, GenPshGlslOptions opts);
+28 -12
View File
@@ -451,21 +451,37 @@ MString *pgraph_glsl_gen_vsh(const VshState *state, GenVshGlslOptions opts)
opts.gles_version);
if (opts.vulkan) {
// FIXME: Optimize uniforms
if (num_uniform_attrs > 0 &&
opts.use_push_constants_for_uniform_attrs) {
mstring_append_fmt(output,
"layout(push_constant) uniform PushConstants {\n"
" vec4 inlineValue[%d];\n"
"};\n\n",
num_uniform_attrs);
if (opts.vertex_push_offset > 0) {
mstring_append_fmt(output,
"layout(push_constant) uniform PushConstants {\n"
" layout(offset = %d) vec4 inlineValue[%d];\n"
"};\n\n",
opts.vertex_push_offset, num_uniform_attrs);
} else {
mstring_append_fmt(output,
"layout(push_constant) uniform PushConstants {\n"
" vec4 inlineValue[%d];\n"
"};\n\n",
num_uniform_attrs);
}
}
if (opts.ubo_set > 0) {
mstring_append_fmt(
output,
"layout(set = %d, binding = %d, std140) uniform VshUniforms {\n"
"%s"
"};\n\n",
opts.ubo_set, opts.ubo_binding, mstring_get_str(uniforms));
} else {
mstring_append_fmt(
output,
"layout(binding = %d, std140) uniform VshUniforms {\n"
"%s"
"};\n\n",
opts.ubo_binding, mstring_get_str(uniforms));
}
mstring_append_fmt(
output,
"layout(binding = %d, std140) uniform VshUniforms {\n"
"%s"
"};\n\n",
opts.ubo_binding, mstring_get_str(uniforms));
} else {
mstring_append(
output, mstring_get_str(uniforms));
+2
View File
@@ -103,6 +103,8 @@ typedef struct GenVshGlslOptions {
bool prefix_outputs;
bool use_push_constants_for_uniform_attrs;
int ubo_binding;
int ubo_set;
int vertex_push_offset;
} GenVshGlslOptions;
MString *pgraph_glsl_gen_vsh(const VshState *state,
+117 -13
View File
@@ -1107,26 +1107,60 @@ static void create_pipeline(PGRAPHState *pg)
// }
VkPushConstantRange push_constant_ranges[2];
int num_push_constant_ranges = 0;
VkPipelineLayoutCreateInfo pipeline_layout_info = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &r->descriptor_set_layout,
};
VkPushConstantRange push_constant_range;
#if OPT_BINDLESS_TEXTURES
VkDescriptorSetLayout set_layouts[2];
if (r->bindless_textures_supported) {
set_layouts[0] = r->bindless_set_layout;
set_layouts[1] = r->descriptor_set_layout;
pipeline_layout_info.setLayoutCount = 2;
pipeline_layout_info.pSetLayouts = set_layouts;
push_constant_ranges[num_push_constant_ranges++] =
(VkPushConstantRange){
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
.offset = r->tex_push_offset,
.size = NV2A_MAX_TEXTURES * sizeof(uint32_t),
};
}
#endif
if (r->use_push_constants_for_uniform_attrs) {
int num_uniform_attributes =
__builtin_popcount(r->shader_binding->state.vsh.uniform_attrs);
if (num_uniform_attributes) {
push_constant_range = (VkPushConstantRange){
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
// FIXME: Minimize push constants
.size = num_uniform_attributes * 4 * sizeof(float),
};
pipeline_layout_info.pushConstantRangeCount = 1;
pipeline_layout_info.pPushConstantRanges = &push_constant_range;
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported &&
num_uniform_attributes > r->max_vertex_push_attrs) {
num_uniform_attributes = 0;
}
#endif
if (num_uniform_attributes) {
#if OPT_BINDLESS_TEXTURES
uint32_t vertex_push_offset =
r->bindless_textures_supported && r->tex_push_offset == 0
? NV2A_MAX_TEXTURES * sizeof(uint32_t)
: 0;
#else
uint32_t vertex_push_offset = 0;
#endif
push_constant_ranges[num_push_constant_ranges++] =
(VkPushConstantRange){
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = vertex_push_offset,
.size = num_uniform_attributes * 4 * sizeof(float),
};
}
}
if (num_push_constant_ranges > 0) {
pipeline_layout_info.pushConstantRangeCount = num_push_constant_ranges;
pipeline_layout_info.pPushConstantRanges = push_constant_ranges;
}
VkPipelineLayout layout;
@@ -1168,11 +1202,26 @@ static void create_pipeline(PGRAPHState *pg)
NV2A_VK_DGROUP_END();
}
static bool can_push_vertex_attr_values(PGRAPHVkState *r)
{
if (!r->use_push_constants_for_uniform_attrs) {
return false;
}
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported &&
__builtin_popcount(r->shader_binding->state.vsh.uniform_attrs) >
r->max_vertex_push_attrs) {
return false;
}
#endif
return true;
}
static void push_vertex_attr_values(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
if (!r->use_push_constants_for_uniform_attrs) {
if (!can_push_vertex_attr_values(r)) {
return;
}
@@ -1185,8 +1234,16 @@ static void push_vertex_attr_values(PGRAPHState *pg)
values, &num_uniform_attrs);
if (num_uniform_attrs > 0) {
#if OPT_BINDLESS_TEXTURES
uint32_t vertex_push_offset =
r->bindless_textures_supported && r->tex_push_offset == 0
? NV2A_MAX_TEXTURES * sizeof(uint32_t)
: 0;
#else
uint32_t vertex_push_offset = 0;
#endif
vkCmdPushConstants(r->command_buffer, r->pipeline_binding->layout,
VK_SHADER_STAGE_VERTEX_BIT, 0,
VK_SHADER_STAGE_VERTEX_BIT, vertex_push_offset,
num_uniform_attrs * 4 * sizeof(float),
&values);
}
@@ -1198,11 +1255,46 @@ static void bind_descriptor_sets(PGRAPHState *pg)
assert(r->descriptor_set_index >= 1);
vkCmdBindDescriptorSets(r->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
r->pipeline_binding->layout, 0, 1,
r->pipeline_binding->layout,
#if OPT_BINDLESS_TEXTURES
r->bindless_textures_supported ? 1 : 0,
#else
0,
#endif
1,
&r->descriptor_sets[r->descriptor_set_index - 1], 0,
NULL);
}
#if OPT_BINDLESS_TEXTURES
static void bind_bindless_set(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
if (!r->bindless_textures_supported) {
return;
}
vkCmdBindDescriptorSets(r->command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
r->pipeline_binding->layout, 0, 1,
&r->bindless_descriptor_set, 0, NULL);
}
static void push_texture_indices(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
if (!r->bindless_textures_supported) {
return;
}
vkCmdPushConstants(r->command_buffer, r->pipeline_binding->layout,
VK_SHADER_STAGE_FRAGMENT_BIT, r->tex_push_offset,
sizeof(r->tex_bindless_indices),
r->tex_bindless_indices);
}
#endif
static void begin_query(PGRAPHVkState *r)
{
assert(r->in_command_buffer);
@@ -1490,6 +1582,9 @@ void pgraph_vk_begin_command_buffer(PGRAPHState *pg)
&command_buffer_begin_info));
r->command_buffer_start_time = pg->draw_time;
r->in_command_buffer = true;
#if OPT_BINDLESS_TEXTURES
r->bindless_set_bound = false;
#endif
}
// FIXME: Refactor below
@@ -1699,8 +1794,17 @@ static void begin_draw(PGRAPHState *pg)
}
if (!pg->clearing && !r->pre_draw_skipped) {
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported && !r->bindless_set_bound) {
bind_bindless_set(pg);
r->bindless_set_bound = true;
}
#endif
bind_descriptor_sets(pg);
push_vertex_attr_values(pg);
#if OPT_BINDLESS_TEXTURES
push_texture_indices(pg);
#endif
}
r->in_draw = true;
+86
View File
@@ -366,6 +366,16 @@ static void add_optional_device_extension_names(
r->memory_budget_extension_enabled = add_extension_if_available(
available_extensions, enabled_extension_names,
VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
#if OPT_BINDLESS_TEXTURES
if (r->device_props.apiVersion >= VK_API_VERSION_1_2) {
r->bindless_textures_supported = true;
} else {
r->bindless_textures_supported = add_extension_if_available(
available_extensions, enabled_extension_names,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
}
#endif
}
static bool check_device_support_required_extensions(VkPhysicalDevice device)
@@ -1051,6 +1061,82 @@ static bool create_logical_device(PGRAPHState *pg, Error **errp)
void *next_struct = NULL;
#if OPT_BINDLESS_TEXTURES
VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features;
if (r->bindless_textures_supported) {
VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_query = {
.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES,
};
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = {
.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES,
};
VkPhysicalDeviceFeatures2 features2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &descriptor_indexing_query,
};
VkPhysicalDeviceProperties2 props2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &descriptor_indexing_props,
};
vkGetPhysicalDeviceFeatures2(r->physical_device, &features2);
vkGetPhysicalDeviceProperties2(r->physical_device, &props2);
bool have_all =
descriptor_indexing_query.descriptorBindingPartiallyBound &&
descriptor_indexing_query
.descriptorBindingSampledImageUpdateAfterBind &&
descriptor_indexing_props
.maxPerStageDescriptorUpdateAfterBindSampledImages >=
MAX_BINDLESS_TEXTURES;
if (have_all) {
memset(&descriptor_indexing_features, 0,
sizeof(descriptor_indexing_features));
descriptor_indexing_features.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;
descriptor_indexing_features.descriptorBindingPartiallyBound =
VK_TRUE;
descriptor_indexing_features
.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE;
descriptor_indexing_features.pNext = next_struct;
next_struct = &descriptor_indexing_features;
if (r->device_props.limits.maxPushConstantsSize >=
NV2A_VERTEXSHADER_ATTRIBUTES * 4 * sizeof(float) +
NV2A_MAX_TEXTURES * sizeof(uint32_t)) {
r->tex_push_offset =
NV2A_VERTEXSHADER_ATTRIBUTES * 4 * sizeof(float);
r->max_vertex_push_attrs = NV2A_VERTEXSHADER_ATTRIBUTES;
} else {
r->tex_push_offset = 0;
r->max_vertex_push_attrs =
NV2A_VERTEXSHADER_ATTRIBUTES - 1;
}
fprintf(stderr,
"Bindless textures enabled (push offset=%u, "
"max vertex attrs=%d, max samplers=%u)\n",
r->tex_push_offset, r->max_vertex_push_attrs,
descriptor_indexing_props
.maxPerStageDescriptorUpdateAfterBindSampledImages);
} else {
r->bindless_textures_supported = false;
fprintf(stderr,
"Bindless textures disabled (partiallyBound=%d, "
"updateAfterBind=%d, maxSamplers=%u)\n",
descriptor_indexing_query
.descriptorBindingPartiallyBound,
descriptor_indexing_query
.descriptorBindingSampledImageUpdateAfterBind,
descriptor_indexing_props
.maxPerStageDescriptorUpdateAfterBindSampledImages);
}
}
#endif
VkPhysicalDeviceCustomBorderColorFeaturesEXT custom_border_features;
if (r->custom_border_color_extension_enabled) {
custom_border_features = (VkPhysicalDeviceCustomBorderColorFeaturesEXT){
+22
View File
@@ -46,6 +46,9 @@
#define NUM_GFX_DESCRIPTOR_SETS 8192
#define MAX_FRAMEBUFFERS 256
#define FB_CACHE_MAX 32
#define OPT_BINDLESS_TEXTURES 1
#define MAX_BINDLESS_TEXTURES 1024
#define BINDLESS_STAGE_SLOT_BASE (MAX_BINDLESS_TEXTURES - NV2A_MAX_TEXTURES)
typedef struct QueueFamilyIndices {
int queue_family;
@@ -247,6 +250,10 @@ typedef struct TextureBinding {
uint32_t submit_time;
unsigned int dirty_check_frame;
bool dirty_check_result;
#if OPT_BINDLESS_TEXTURES
uint32_t bindless_slot;
uint32_t bindless_binding;
#endif
} TextureBinding;
typedef struct QueryReport {
@@ -366,6 +373,11 @@ typedef struct PGRAPHVkState {
bool debug_utils_extension_enabled;
bool custom_border_color_extension_enabled;
bool memory_budget_extension_enabled;
#if OPT_BINDLESS_TEXTURES
bool bindless_textures_supported;
uint32_t tex_push_offset;
int max_vertex_push_attrs;
#endif
VkPhysicalDevice physical_device;
VkPhysicalDeviceFeatures enabled_physical_device_features;
@@ -383,6 +395,9 @@ typedef struct PGRAPHVkState {
VkFence command_buffer_fence;
unsigned int command_buffer_start_time;
bool in_command_buffer;
#if OPT_BINDLESS_TEXTURES
bool bindless_set_bound;
#endif
uint32_t submit_count;
VkCommandBuffer aux_command_buffer;
@@ -419,6 +434,13 @@ typedef struct PGRAPHVkState {
VkDescriptorSet *descriptor_sets;
int descriptor_set_count;
int descriptor_set_index;
#if OPT_BINDLESS_TEXTURES
VkDescriptorPool bindless_descriptor_pool;
VkDescriptorSetLayout bindless_set_layout;
VkDescriptorSet bindless_descriptor_set;
uint64_t bindless_slot_bitmap[MAX_BINDLESS_TEXTURES / 64];
uint32_t tex_bindless_indices[NV2A_MAX_TEXTURES];
#endif
StorageBuffer storage_buffers[BUFFER_COUNT];
PrimRewriteBuf prim_rewrite_buf;
+226 -27
View File
@@ -34,6 +34,28 @@ static void create_descriptor_pool(PGRAPHState *pg)
size_t num_sets = r->descriptor_set_count;
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported) {
VkDescriptorPoolSize pool_sizes[] = {
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = 2 * num_sets,
},
};
VkDescriptorPoolCreateInfo pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.poolSizeCount = ARRAY_SIZE(pool_sizes),
.pPoolSizes = pool_sizes,
.maxSets = num_sets,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
};
VK_CHECK(vkCreateDescriptorPool(r->device, &pool_info, NULL,
&r->descriptor_pool));
return;
}
#endif
VkDescriptorPoolSize pool_sizes[] = {
{
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
@@ -68,6 +90,33 @@ static void create_descriptor_set_layout(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported) {
VkDescriptorSetLayoutBinding bindings[2] = {
{
.binding = VSH_UBO_BINDING,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
},
{
.binding = PSH_UBO_BINDING,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
},
};
VkDescriptorSetLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = ARRAY_SIZE(bindings),
.pBindings = bindings,
};
VK_CHECK(vkCreateDescriptorSetLayout(r->device, &layout_info, NULL,
&r->descriptor_set_layout));
return;
}
#endif
VkDescriptorSetLayoutBinding bindings[2 + NV2A_MAX_TEXTURES];
bindings[0] = (VkDescriptorSetLayoutBinding){
@@ -145,6 +194,109 @@ static void destroy_descriptor_sets(PGRAPHState *pg)
r->descriptor_sets = NULL;
}
#if OPT_BINDLESS_TEXTURES
static void create_bindless_descriptor_resources(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
if (!r->bindless_textures_supported) {
return;
}
VkDescriptorSetLayoutBinding bindings[3];
VkDescriptorBindingFlags binding_flags[3];
for (int i = 0; i < 3; i++) {
bindings[i] = (VkDescriptorSetLayoutBinding){
.binding = (uint32_t)i,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = MAX_BINDLESS_TEXTURES,
.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
};
binding_flags[i] =
VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT |
VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;
}
VkDescriptorSetLayoutBindingFlagsCreateInfo flags_info = {
.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO,
.bindingCount = ARRAY_SIZE(binding_flags),
.pBindingFlags = binding_flags,
};
VkDescriptorSetLayoutCreateInfo layout_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = &flags_info,
.flags =
VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT,
.bindingCount = ARRAY_SIZE(bindings),
.pBindings = bindings,
};
VK_CHECK(vkCreateDescriptorSetLayout(r->device, &layout_info, NULL,
&r->bindless_set_layout));
VkDescriptorPoolSize pool_size = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 3 * MAX_BINDLESS_TEXTURES,
};
VkDescriptorPoolCreateInfo pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT,
.maxSets = 1,
.poolSizeCount = 1,
.pPoolSizes = &pool_size,
};
VK_CHECK(vkCreateDescriptorPool(r->device, &pool_info, NULL,
&r->bindless_descriptor_pool));
VkDescriptorSetAllocateInfo alloc_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = r->bindless_descriptor_pool,
.descriptorSetCount = 1,
.pSetLayouts = &r->bindless_set_layout,
};
VK_CHECK(vkAllocateDescriptorSets(r->device, &alloc_info,
&r->bindless_descriptor_set));
memset(r->bindless_slot_bitmap, 0, sizeof(r->bindless_slot_bitmap));
memset(r->tex_bindless_indices, 0, sizeof(r->tex_bindless_indices));
r->bindless_slot_bitmap[0] |= 1ULL;
for (uint32_t slot = BINDLESS_STAGE_SLOT_BASE;
slot < MAX_BINDLESS_TEXTURES; slot++) {
r->bindless_slot_bitmap[slot / 64] |= (1ULL << (slot % 64));
}
}
static void destroy_bindless_descriptor_resources(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
if (!r->bindless_textures_supported) {
return;
}
vkDestroyDescriptorPool(r->device, r->bindless_descriptor_pool, NULL);
vkDestroyDescriptorSetLayout(r->device, r->bindless_set_layout, NULL);
r->bindless_descriptor_pool = VK_NULL_HANDLE;
r->bindless_set_layout = VK_NULL_HANDLE;
r->bindless_descriptor_set = VK_NULL_HANDLE;
}
#endif
static bool can_use_vertex_push_constants(PGRAPHVkState *r,
const VshState *state)
{
if (!r->use_push_constants_for_uniform_attrs) {
return false;
}
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported &&
__builtin_popcount(state->uniform_attrs) > r->max_vertex_push_attrs) {
return false;
}
#endif
return true;
}
void pgraph_vk_update_descriptor_sets(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
@@ -153,7 +305,12 @@ void pgraph_vk_update_descriptor_sets(PGRAPHState *pg)
r->uniforms_changed ||
!r->storage_buffers[BUFFER_UNIFORM_STAGING].buffer_offset;
if (!(r->shader_bindings_changed || r->texture_bindings_changed ||
if (!(r->shader_bindings_changed ||
#if OPT_BINDLESS_TEXTURES
(!r->bindless_textures_supported && r->texture_bindings_changed) ||
#else
r->texture_bindings_changed ||
#endif
(r->descriptor_set_index == 0) || need_uniform_write)) {
return; // Nothing changed
}
@@ -179,8 +336,6 @@ void pgraph_vk_update_descriptor_sets(PGRAPHState *pg)
need_uniform_write = true;
}
VkWriteDescriptorSet descriptor_writes[2 + NV2A_MAX_TEXTURES];
assert(r->descriptor_set_index < r->descriptor_set_count);
if (need_uniform_write) {
@@ -196,6 +351,7 @@ void pgraph_vk_update_descriptor_sets(PGRAPHState *pg)
}
VkDescriptorBufferInfo ubo_buffer_infos[2];
VkWriteDescriptorSet descriptor_writes[2 + NV2A_MAX_TEXTURES];
for (int i = 0; i < ARRAY_SIZE(layouts); i++) {
ubo_buffer_infos[i] = (VkDescriptorBufferInfo){
.buffer = r->storage_buffers[BUFFER_UNIFORM].buffer,
@@ -213,29 +369,37 @@ void pgraph_vk_update_descriptor_sets(PGRAPHState *pg)
};
}
VkDescriptorImageInfo image_infos[NV2A_MAX_TEXTURES];
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
image_infos[i] = (VkDescriptorImageInfo){
.imageLayout = r->tex_surface_direct[i]
? r->tex_surface_direct_layout[i]
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = r->tex_surface_direct[i]
? r->tex_surface_direct_views[i]
: r->texture_bindings[i]->image_view,
.sampler = r->texture_bindings[i]->sampler,
};
descriptor_writes[2 + i] = (VkWriteDescriptorSet){
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = r->descriptor_sets[r->descriptor_set_index],
.dstBinding = PSH_TEX_BINDING + i,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &image_infos[i],
};
}
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported) {
vkUpdateDescriptorSets(r->device, 2, descriptor_writes, 0, NULL);
} else
#endif
{
VkDescriptorImageInfo image_infos[NV2A_MAX_TEXTURES];
for (int i = 0; i < NV2A_MAX_TEXTURES; i++) {
image_infos[i] = (VkDescriptorImageInfo){
.imageLayout = r->tex_surface_direct[i]
? r->tex_surface_direct_layout[i]
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.imageView = r->tex_surface_direct[i]
? r->tex_surface_direct_views[i]
: r->texture_bindings[i]->image_view,
.sampler = r->texture_bindings[i]->sampler,
};
descriptor_writes[2 + i] = (VkWriteDescriptorSet){
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = r->descriptor_sets[r->descriptor_set_index],
.dstBinding = PSH_TEX_BINDING + i,
.dstArrayElement = 0,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &image_infos[i],
};
}
vkUpdateDescriptorSets(r->device, 6, descriptor_writes, 0, NULL);
vkUpdateDescriptorSets(r->device, 2 + NV2A_MAX_TEXTURES,
descriptor_writes, 0, NULL);
}
r->descriptor_set_index++;
}
@@ -293,8 +457,18 @@ static void shader_cache_entry_init(Lru *lru, LruNode *node, const void *state)
key.vsh.glsl_opts.vulkan = true;
key.vsh.glsl_opts.prefix_outputs = need_geometry_shader;
key.vsh.glsl_opts.use_push_constants_for_uniform_attrs =
r->use_push_constants_for_uniform_attrs;
can_use_vertex_push_constants(r, &binding->state.vsh);
key.vsh.glsl_opts.ubo_binding = VSH_UBO_BINDING;
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported) {
key.vsh.glsl_opts.ubo_set = 1;
if (key.vsh.glsl_opts.use_push_constants_for_uniform_attrs &&
r->tex_push_offset == 0) {
key.vsh.glsl_opts.vertex_push_offset =
NV2A_MAX_TEXTURES * sizeof(uint32_t);
}
}
#endif
binding->vsh.module_info = get_and_ref_shader_module_for_key(r, &key);
memset(&key, 0, sizeof(key));
@@ -302,7 +476,16 @@ static void shader_cache_entry_init(Lru *lru, LruNode *node, const void *state)
key.psh.state = binding->state.psh;
key.psh.glsl_opts.vulkan = true;
key.psh.glsl_opts.ubo_binding = PSH_UBO_BINDING;
key.psh.glsl_opts.tex_binding = PSH_TEX_BINDING;
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported) {
key.psh.glsl_opts.ubo_set = 1;
key.psh.glsl_opts.bindless = true;
key.psh.glsl_opts.tex_push_offset = r->tex_push_offset;
} else
#endif
{
key.psh.glsl_opts.tex_binding = PSH_TEX_BINDING;
}
binding->psh.module_info = get_and_ref_shader_module_for_key(r, &key);
update_shader_uniform_locs(binding);
@@ -548,16 +731,32 @@ void pgraph_vk_init_shaders(PGRAPHState *pg)
create_descriptor_pool(pg);
create_descriptor_set_layout(pg);
create_descriptor_sets(pg);
#if OPT_BINDLESS_TEXTURES
create_bindless_descriptor_resources(pg);
#endif
shader_cache_init(pg);
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported) {
size_t vtx_budget = r->max_vertex_push_attrs * 4 * sizeof(float);
r->use_push_constants_for_uniform_attrs =
(r->device_props.limits.maxPushConstantsSize >=
vtx_budget + NV2A_MAX_TEXTURES * sizeof(uint32_t));
} else
#endif
{
r->use_push_constants_for_uniform_attrs =
(r->device_props.limits.maxPushConstantsSize >=
MAX_UNIFORM_ATTR_VALUES_SIZE);
}
}
void pgraph_vk_finalize_shaders(PGRAPHState *pg)
{
shader_cache_finalize(pg);
#if OPT_BINDLESS_TEXTURES
destroy_bindless_descriptor_resources(pg);
#endif
destroy_descriptor_sets(pg);
destroy_descriptor_set_layout(pg);
destroy_descriptor_pool(pg);
+129
View File
@@ -1308,6 +1308,17 @@ static bool check_surface_to_texture_compatiblity(const SurfaceBinding *surface,
surface->host_fmt.host_bytes_per_pixel == vk_format_texel_size(tex_vkf.vk_format);
}
#if OPT_BINDLESS_TEXTURES
static void update_bindless_texture_descriptor(PGRAPHVkState *r,
uint32_t binding,
uint32_t slot,
VkImageView image_view,
VkImageLayout image_layout,
VkSampler sampler);
static void set_texture_bindless_index(PGRAPHState *pg, int texture_idx,
TextureBinding *binding);
#endif
static void create_dummy_texture(PGRAPHState *pg)
{
PGRAPHVkState *r = pg->vk_renderer_state;
@@ -1433,7 +1444,17 @@ static void create_dummy_texture(PGRAPHState *pg)
.allocation = texture_allocation,
.image_view = texture_image_view,
.sampler = texture_sampler,
#if OPT_BINDLESS_TEXTURES
.bindless_slot = 0,
.bindless_binding = 0,
#endif
};
#if OPT_BINDLESS_TEXTURES
update_bindless_texture_descriptor(
r, 0, 0, texture_image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
texture_sampler);
#endif
}
static void destroy_dummy_texture(PGRAPHVkState *r)
@@ -1441,6 +1462,60 @@ static void destroy_dummy_texture(PGRAPHVkState *r)
texture_cache_release_node_resources(r, &r->dummy_texture);
}
#if OPT_BINDLESS_TEXTURES
static void update_bindless_texture_descriptor(PGRAPHVkState *r,
uint32_t binding,
uint32_t slot,
VkImageView image_view,
VkImageLayout image_layout,
VkSampler sampler)
{
if (!r->bindless_textures_supported) {
return;
}
VkDescriptorImageInfo image_info = {
.imageLayout = image_layout,
.imageView = image_view,
.sampler = sampler,
};
VkWriteDescriptorSet write = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = r->bindless_descriptor_set,
.dstBinding = binding,
.dstArrayElement = slot,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.pImageInfo = &image_info,
};
vkUpdateDescriptorSets(r->device, 1, &write, 0, NULL);
}
static void set_texture_bindless_index(PGRAPHState *pg, int texture_idx,
TextureBinding *binding)
{
PGRAPHVkState *r = pg->vk_renderer_state;
uint32_t slot = 0;
if (!r->bindless_textures_supported) {
return;
}
if (binding != NULL) {
slot = binding->bindless_slot;
if (r->tex_surface_direct[texture_idx]) {
slot = BINDLESS_STAGE_SLOT_BASE + texture_idx;
update_bindless_texture_descriptor(
r, binding->bindless_binding, slot,
r->tex_surface_direct_views[texture_idx],
r->tex_surface_direct_layout[texture_idx], binding->sampler);
}
}
r->tex_bindless_indices[texture_idx] = slot;
}
#endif
static void set_texture_label(PGRAPHState *pg, TextureBinding *texture)
{
PGRAPHVkState *r = pg->vk_renderer_state;
@@ -1655,6 +1730,9 @@ static void create_texture(PGRAPHState *pg, int texture_idx)
}
}
#if OPT_BINDLESS_TEXTURES
set_texture_bindless_index(pg, texture_idx, snode);
#endif
NV2A_VK_DGROUP_END();
return;
}
@@ -1852,6 +1930,22 @@ static void create_texture(PGRAPHState *pg, int texture_idx)
set_texture_label(pg, snode);
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported) {
if (state.cubemap) {
snode->bindless_binding = 2;
} else if (state.dimensionality == 3) {
snode->bindless_binding = 1;
} else {
snode->bindless_binding = 0;
}
update_bindless_texture_descriptor(
r, snode->bindless_binding, snode->bindless_slot,
snode->image_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
snode->sampler);
}
#endif
r->texture_bindings[texture_idx] = snode;
if (surface_to_texture) {
@@ -1878,6 +1972,9 @@ static void create_texture(PGRAPHState *pg, int texture_idx)
snode->draw_time = 0;
}
#if OPT_BINDLESS_TEXTURES
set_texture_bindless_index(pg, texture_idx, snode);
#endif
NV2A_VK_DGROUP_END();
}
@@ -1973,6 +2070,9 @@ void pgraph_vk_bind_textures(NV2AState *d)
r->tex_surface_direct_layout[i] =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
pg->texture_dirty[i] = false;
#if OPT_BINDLESS_TEXTURES
r->tex_bindless_indices[i] = 0;
#endif
continue;
}
@@ -2004,6 +2104,7 @@ void pgraph_vk_bind_textures(NV2AState *d)
static void texture_cache_entry_init(Lru *lru, LruNode *node, const void *state)
{
PGRAPHVkState *r = container_of(lru, PGRAPHVkState, texture_cache);
TextureBinding *snode = container_of(node, TextureBinding, node);
snode->image = VK_NULL_HANDLE;
@@ -2015,6 +2116,27 @@ static void texture_cache_entry_init(Lru *lru, LruNode *node, const void *state)
snode->submit_time = 0;
snode->dirty_check_frame = 0;
snode->dirty_check_result = false;
#if OPT_BINDLESS_TEXTURES
snode->bindless_slot = 0;
snode->bindless_binding = 0;
if (r->bindless_textures_supported) {
for (uint32_t word = 0;
word < ARRAY_SIZE(r->bindless_slot_bitmap); word++) {
if (r->bindless_slot_bitmap[word] == UINT64_MAX) {
continue;
}
uint32_t bit = __builtin_ctzll(~r->bindless_slot_bitmap[word]);
uint32_t slot = word * 64 + bit;
if (slot >= BINDLESS_STAGE_SLOT_BASE) {
break;
}
snode->bindless_slot = slot;
r->bindless_slot_bitmap[word] |= (1ULL << bit);
break;
}
assert(snode->bindless_slot > 0);
}
#endif
}
static void texture_cache_release_node_resources(PGRAPHVkState *r, TextureBinding *snode)
@@ -2061,6 +2183,13 @@ static void texture_cache_entry_post_evict(Lru *lru, LruNode *node)
{
PGRAPHVkState *r = container_of(lru, PGRAPHVkState, texture_cache);
TextureBinding *snode = container_of(node, TextureBinding, node);
#if OPT_BINDLESS_TEXTURES
if (r->bindless_textures_supported && snode->bindless_slot > 0 &&
snode->bindless_slot < BINDLESS_STAGE_SLOT_BASE) {
r->bindless_slot_bitmap[snode->bindless_slot / 64] &=
~(1ULL << (snode->bindless_slot % 64));
}
#endif
texture_cache_release_node_resources(r, snode);
}