diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.c b/hw/xbox/nv2a/pgraph/glsl/psh.c index a254db8630..3c3a7f6ef6 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.c +++ b/hw/xbox/nv2a/pgraph/glsl/psh.c @@ -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]) { diff --git a/hw/xbox/nv2a/pgraph/glsl/psh.h b/hw/xbox/nv2a/pgraph/glsl/psh.h index e18cac86c5..bef84ec287 100644 --- a/hw/xbox/nv2a/pgraph/glsl/psh.h +++ b/hw/xbox/nv2a/pgraph/glsl/psh.h @@ -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); diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh.c b/hw/xbox/nv2a/pgraph/glsl/vsh.c index 467a3873f9..ab041399bb 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh.c @@ -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)); diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh.h b/hw/xbox/nv2a/pgraph/glsl/vsh.h index faabd9bbd5..43d46a0f57 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh.h +++ b/hw/xbox/nv2a/pgraph/glsl/vsh.h @@ -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, diff --git a/hw/xbox/nv2a/pgraph/vk/draw.c b/hw/xbox/nv2a/pgraph/vk/draw.c index 4d1a2f64cb..6fadba8cef 100644 --- a/hw/xbox/nv2a/pgraph/vk/draw.c +++ b/hw/xbox/nv2a/pgraph/vk/draw.c @@ -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; diff --git a/hw/xbox/nv2a/pgraph/vk/instance.c b/hw/xbox/nv2a/pgraph/vk/instance.c index b3ce56245b..400f443c26 100644 --- a/hw/xbox/nv2a/pgraph/vk/instance.c +++ b/hw/xbox/nv2a/pgraph/vk/instance.c @@ -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){ diff --git a/hw/xbox/nv2a/pgraph/vk/renderer.h b/hw/xbox/nv2a/pgraph/vk/renderer.h index f01b1648f9..7cddf864de 100644 --- a/hw/xbox/nv2a/pgraph/vk/renderer.h +++ b/hw/xbox/nv2a/pgraph/vk/renderer.h @@ -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; diff --git a/hw/xbox/nv2a/pgraph/vk/shaders.c b/hw/xbox/nv2a/pgraph/vk/shaders.c index 1e20caba7a..01b6fbd7ec 100644 --- a/hw/xbox/nv2a/pgraph/vk/shaders.c +++ b/hw/xbox/nv2a/pgraph/vk/shaders.c @@ -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); diff --git a/hw/xbox/nv2a/pgraph/vk/texture.c b/hw/xbox/nv2a/pgraph/vk/texture.c index b6e2758308..1aac7075e5 100644 --- a/hw/xbox/nv2a/pgraph/vk/texture.c +++ b/hw/xbox/nv2a/pgraph/vk/texture.c @@ -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); }