vk: Update shader interpreter to use dynamic binding layout

This commit is contained in:
kd-11 2025-06-22 18:28:35 +03:00
parent 27d6397eed
commit a176909a2b
11 changed files with 200 additions and 386 deletions

View file

@ -20,8 +20,9 @@ namespace program_common
COMPILER_OPT_ENABLE_KIL = (1 << 11),
COMPILER_OPT_ENABLE_STIPPLING = (1 << 12),
COMPILER_OPT_ENABLE_INSTANCING = (1 << 13),
COMPILER_OPT_ENABLE_VTX_TEXTURES = (1 << 14),
COMPILER_OPT_MAX = COMPILER_OPT_ENABLE_INSTANCING
COMPILER_OPT_MAX = COMPILER_OPT_ENABLE_VTX_TEXTURES
};
static std::string get_vertex_interpreter()

View file

@ -8,157 +8,6 @@
namespace vk
{
rsx::simple_array<VkDescriptorSetLayoutBinding> get_common_binding_table()
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
rsx::simple_array<VkDescriptorSetLayoutBinding> bindings(binding_table.instancing_constants_buffer_slot + 1);
u32 idx = 0;
// Vertex stream, one stream for cacheable data, one stream for transient data
for (int i = 0; i < 3; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_buffers_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_state_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_texture_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_constant_buffers_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
bindings[idx].binding = binding_table.vertex_params_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.conditional_render_predicate_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.rasterizer_env_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_lookup_table_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.instancing_constants_buffer_slot;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
return bindings;
}
std::tuple<VkPipelineLayout, VkDescriptorSetLayout, rsx::simple_array<VkDescriptorSetLayoutBinding>>
get_common_pipeline_layout(VkDevice dev)
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
auto bindings = get_common_binding_table();
u32 idx = ::size32(bindings);
bindings.resize(binding_table.total_descriptor_bindings);
for (auto binding = binding_table.textures_first_bind_slot;
binding < binding_table.vertex_textures_first_bind_slot;
binding++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
for (int i = 0; i < rsx::limits::vertex_textures_count; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_textures_first_bind_slot + i;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
}
ensure(idx == binding_table.total_descriptor_bindings);
std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0;
push_constants[0].size = 20;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vk::emulate_conditional_rendering())
{
// Conditional render toggle
push_constants[0].size = 24;
}
const auto set_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants.data();
VkPipelineLayout result;
CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result));
return std::make_tuple(result, set_layout, bindings);
}
rsx::simple_array<VkDescriptorPoolSize> get_descriptor_pool_sizes(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings)
{
// Compile descriptor pool sizes

View file

@ -5,13 +5,6 @@
namespace vk
{
// Grab standard layout for decompiled RSX programs. Also used by the interpreter.
// FIXME: This generates a bloated monstrosity that needs to die.
std::tuple<VkPipelineLayout, VkDescriptorSetLayout, rsx::simple_array<VkDescriptorSetLayoutBinding>> get_common_pipeline_layout(VkDevice dev);
// Returns the standard binding layout without texture slots. Those have special handling depending on the consumer.
rsx::simple_array<VkDescriptorSetLayoutBinding> get_common_binding_table();
// Returns an array of pool sizes that can be used to generate a proper descriptor pool
rsx::simple_array<VkDescriptorPoolSize> get_descriptor_pool_sizes(const rsx::simple_array<VkDescriptorSetLayoutBinding>& bindings);
}

View file

@ -555,7 +555,7 @@ bool VKGSRender::bind_texture_env()
{
m_program->bind_uniform({ fs_sampler_handles[i]->value, view->value, view->image()->current_layout },
vk::glsl::binding_set_index_fragment,
m_fragment_prog->binding_table.ftex_location[i]);
m_fs_binding_table->ftex_location[i]);
if (current_fragment_program.texture_state.redirected_textures & (1 << i))
{
@ -576,7 +576,7 @@ bool VKGSRender::bind_texture_env()
m_program->bind_uniform({ m_stencil_mirror_sampler->value, stencil_view->value, stencil_view->image()->current_layout },
vk::glsl::binding_set_index_fragment,
m_fragment_prog->binding_table.ftex_stencil_location[i]);
m_fs_binding_table->ftex_stencil_location[i]);
}
}
else
@ -584,13 +584,13 @@ bool VKGSRender::bind_texture_env()
const VkImageViewType view_type = vk::get_view_type(current_fragment_program.get_texture_dimension(i));
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_fragment,
m_fragment_prog->binding_table.ftex_location[i]);
m_fs_binding_table->ftex_location[i]);
if (current_fragment_program.texture_state.redirected_textures & (1 << i))
{
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_fragment,
m_fragment_prog->binding_table.ftex_stencil_location[i]);
m_fs_binding_table->ftex_stencil_location[i]);
}
}
}
@ -605,7 +605,7 @@ bool VKGSRender::bind_texture_env()
const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i));
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_vertex,
m_vertex_prog->binding_table.vtex_location[i]);
m_vs_binding_table->vtex_location[i]);
continue;
}
@ -628,7 +628,7 @@ bool VKGSRender::bind_texture_env()
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, view_type)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
vk::glsl::binding_set_index_vertex,
m_vertex_prog->binding_table.vtex_location[i]);
m_vs_binding_table->vtex_location[i]);
continue;
}
@ -637,7 +637,7 @@ bool VKGSRender::bind_texture_env()
m_program->bind_uniform({ vs_sampler_handles[i]->value, image_ptr->value, image_ptr->image()->current_layout },
vk::glsl::binding_set_index_vertex,
m_vertex_prog->binding_table.vtex_location[i]);
m_vs_binding_table->vtex_location[i]);
}
return out_of_memory;
@ -874,10 +874,9 @@ void VKGSRender::emit_geometry(u32 sub_index)
ensure(m_vertex_layout_storage);
if (update_descriptors)
{
const auto& binding_table = m_vertex_prog->binding_table;
m_program->bind_uniform(persistent_buffer, vk::glsl::binding_set_index_vertex, binding_table.vertex_buffers_location);
m_program->bind_uniform(volatile_buffer, vk::glsl::binding_set_index_vertex, binding_table.vertex_buffers_location + 1);
m_program->bind_uniform(m_vertex_layout_storage->value, vk::glsl::binding_set_index_vertex, binding_table.vertex_buffers_location + 2);
m_program->bind_uniform(persistent_buffer, vk::glsl::binding_set_index_vertex, m_vs_binding_table->vertex_buffers_location);
m_program->bind_uniform(volatile_buffer, vk::glsl::binding_set_index_vertex, m_vs_binding_table->vertex_buffers_location + 1);
m_program->bind_uniform(m_vertex_layout_storage->value, vk::glsl::binding_set_index_vertex, m_vs_binding_table->vertex_buffers_location + 2);
}
bool reload_state = (!m_current_draw.subdraw_id++);

View file

@ -10,7 +10,7 @@ namespace vk
class shader_interpreter;
}
struct VKFragmentDecompilerThread : public FragmentProgramDecompiler
class VKFragmentDecompilerThread : public FragmentProgramDecompiler
{
friend class vk::shader_interpreter;

View file

@ -1780,8 +1780,11 @@ bool VKGSRender::load_program()
m_program = m_shader_interpreter.get(
m_pipeline_properties,
current_fp_metadata,
current_vp_metadata,
current_vertex_program.ctrl,
current_fragment_program.ctrl);
std::tie(m_vs_binding_table, m_fs_binding_table) = get_binding_table();
return true;
}
}
@ -1879,6 +1882,7 @@ bool VKGSRender::load_program()
m_program = m_shader_interpreter.get(
m_pipeline_properties,
current_fp_metadata,
current_vp_metadata,
current_vertex_program.ctrl,
current_fragment_program.ctrl);
@ -1900,6 +1904,16 @@ bool VKGSRender::load_program()
}
}
if (m_program)
{
std::tie(m_vs_binding_table, m_fs_binding_table) = get_binding_table();
}
else
{
m_vs_binding_table = nullptr;
m_fs_binding_table = nullptr;
}
return m_program != nullptr;
}
@ -1911,13 +1925,14 @@ void VKGSRender::load_program_env()
}
const u32 fragment_constants_size = current_fp_metadata.program_constants_buffer_length;
const bool is_interpreter = m_shader_interpreter.is_interpreter(m_program);
const bool update_transform_constants = !!(m_graphics_state & rsx::pipeline_state::transform_constants_dirty);
const bool update_fragment_constants = !!(m_graphics_state & rsx::pipeline_state::fragment_constants_dirty);
const bool update_vertex_env = !!(m_graphics_state & rsx::pipeline_state::vertex_state_dirty);
const bool update_fragment_env = !!(m_graphics_state & rsx::pipeline_state::fragment_state_dirty);
const bool update_fragment_texture_env = !!(m_graphics_state & rsx::pipeline_state::fragment_texture_state_dirty);
const bool update_instruction_buffers = (!!m_interpreter_state && m_shader_interpreter.is_interpreter(m_program));
const bool update_instruction_buffers = (!!m_interpreter_state && is_interpreter);
const bool update_raster_env = (rsx::method_registers.polygon_stipple_enabled() && !!(m_graphics_state & rsx::pipeline_state::polygon_stipple_pattern_dirty));
const bool update_instancing_data = rsx::method_registers.current_draw_clause.is_trivial_instanced_draw;
@ -2078,17 +2093,14 @@ void VKGSRender::load_program_env()
}
}
const auto& vs_binding_table = m_vertex_prog->binding_table;
const auto& fs_binding_table = m_fragment_prog->binding_table;
m_program->bind_uniform(m_vertex_env_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->context_buffer_location);
m_program->bind_uniform(m_fragment_env_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->context_buffer_location);
m_program->bind_uniform(m_fragment_texture_params_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->tex_param_location);
m_program->bind_uniform(m_raster_env_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->polygon_stipple_params_location);
m_program->bind_uniform(m_vertex_env_buffer_info, vk::glsl::binding_set_index_vertex, vs_binding_table.context_buffer_location);
m_program->bind_uniform(m_fragment_env_buffer_info, vk::glsl::binding_set_index_fragment, fs_binding_table.context_buffer_location);
m_program->bind_uniform(m_fragment_texture_params_buffer_info, vk::glsl::binding_set_index_fragment, fs_binding_table.tex_param_location);
m_program->bind_uniform(m_raster_env_buffer_info, vk::glsl::binding_set_index_fragment, fs_binding_table.polygon_stipple_params_location);
if (vs_binding_table.cbuf_location != umax)
if (m_vs_binding_table->cbuf_location != umax)
{
m_program->bind_uniform(m_vertex_constants_buffer_info, vk::glsl::binding_set_index_vertex, vs_binding_table.cbuf_location);
m_program->bind_uniform(m_vertex_constants_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->cbuf_location);
}
if (m_shader_interpreter.is_interpreter(m_program))
@ -2096,21 +2108,21 @@ void VKGSRender::load_program_env()
m_program->bind_uniform(m_vertex_instructions_buffer_info, vk::glsl::binding_set_index_vertex, m_shader_interpreter.get_vertex_instruction_location());
m_program->bind_uniform(m_fragment_instructions_buffer_info, vk::glsl::binding_set_index_fragment, m_shader_interpreter.get_fragment_instruction_location());
}
else if (fs_binding_table.cbuf_location != umax)
else if (m_fs_binding_table->cbuf_location != umax)
{
m_program->bind_uniform(m_fragment_constants_buffer_info, vk::glsl::binding_set_index_fragment, fs_binding_table.cbuf_location);
m_program->bind_uniform(m_fragment_constants_buffer_info, vk::glsl::binding_set_index_fragment, m_fs_binding_table->cbuf_location);
}
if (vk::emulate_conditional_rendering())
{
auto predicate = m_cond_render_buffer ? m_cond_render_buffer->value : vk::get_scratch_buffer(*m_current_command_buffer, 4)->value;
m_program->bind_uniform({ predicate, 0, 4 }, vk::glsl::binding_set_index_vertex, vs_binding_table.cr_pred_buffer_location);
m_program->bind_uniform({ predicate, 0, 4 }, vk::glsl::binding_set_index_vertex, m_vs_binding_table->cr_pred_buffer_location);
}
if (current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS)
{
m_program->bind_uniform(m_instancing_indirection_buffer_info, vk::glsl::binding_set_index_vertex, vs_binding_table.instanced_lut_buffer_location);
m_program->bind_uniform(m_instancing_constants_array_buffer_info, vk::glsl::binding_set_index_vertex, vs_binding_table.instanced_cbuf_location);
m_program->bind_uniform(m_instancing_indirection_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->instanced_lut_buffer_location);
m_program->bind_uniform(m_instancing_constants_array_buffer_info, vk::glsl::binding_set_index_vertex, m_vs_binding_table->instanced_cbuf_location);
}
// Clear flags
@ -2137,6 +2149,19 @@ void VKGSRender::load_program_env()
m_graphics_state.clear(handled_flags);
}
std::pair<const vs_binding_table_t*, const fs_binding_table_t*> VKGSRender::get_binding_table() const
{
ensure(m_program);
if (!m_shader_interpreter.is_interpreter(m_program))
{
return { &m_vertex_prog->binding_table, &m_fragment_prog->binding_table };
}
const auto& [vs, fs] = m_shader_interpreter.get_shaders();
return { &vs->binding_table, &fs->binding_table };
}
bool VKGSRender::is_current_program_interpreted() const
{
return m_program && m_shader_interpreter.is_interpreter(m_program);

View file

@ -26,6 +26,9 @@
using namespace vk::vmm_allocation_pool_; // clang workaround.
using namespace vk::upscaling_flags_; // ditto
using vs_binding_table_t = decltype(VKVertexProgram::binding_table);
using fs_binding_table_t = decltype(VKFragmentProgram::binding_table);
namespace vk
{
using host_data_t = rsx::host_gpu_context_t;
@ -53,6 +56,9 @@ private:
vk::glsl::program *m_prev_program = nullptr;
vk::pipeline_props m_pipeline_properties;
const vs_binding_table_t* m_vs_binding_table = nullptr;
const fs_binding_table_t* m_fs_binding_table = nullptr;
vk::texture_cache m_texture_cache;
vk::surface_cache m_rtts;
@ -78,6 +84,8 @@ private:
VkDependencyInfoKHR m_async_compute_dependency_info {};
VkMemoryBarrier2KHR m_async_compute_memory_barrier {};
std::pair<const vs_binding_table_t*, const fs_binding_table_t*> get_binding_table() const;
public:
//vk::fbo draw_fbo;
std::unique_ptr<vk::vertex_cache> m_vertex_cache;

View file

@ -12,23 +12,70 @@
namespace vk
{
glsl::shader* shader_interpreter::build_vs(u64 compiler_options)
u32 shader_interpreter::init(VKVertexProgram* vk_prog, u64 compiler_options) const
{
std::memset(&vk_prog->binding_table, 0xff, sizeof(vk_prog->binding_table));
u32 location = 0;
vk_prog->binding_table.vertex_buffers_location = location;
location += 3;
vk_prog->binding_table.context_buffer_location = location++;
if (vk::emulate_conditional_rendering())
{
vk_prog->binding_table.cr_pred_buffer_location = location++;
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING)
{
vk_prog->binding_table.instanced_lut_buffer_location = location++;
vk_prog->binding_table.instanced_cbuf_location = location++;
}
else
{
vk_prog->binding_table.cbuf_location = location++;
}
if (vk::emulate_conditional_rendering())
{
vk_prog->binding_table.cr_pred_buffer_location = location++;
}
// Return next index
return location;
}
u32 shader_interpreter::init(VKFragmentProgram* vk_prog, u64 compiler_opt) const
{
std::memset(&vk_prog->binding_table, 0xff, sizeof(vk_prog->binding_table));
vk_prog->binding_table.context_buffer_location = 0;
vk_prog->binding_table.tex_param_location = 1;
vk_prog->binding_table.polygon_stipple_params_location = 2;
// Return next index
return 3;
}
VKVertexProgram* shader_interpreter::build_vs(u64 compiler_options)
{
::glsl::shader_properties properties{};
properties.domain = ::glsl::program_domain::glsl_vertex_program;
properties.require_lit_emulation = true;
// TODO: Extend decompiler thread
// TODO: Rename decompiler thread, it no longer spawns a thread
RSXVertexProgram null_prog;
std::string shader_str;
ParamArray arr;
VKVertexProgram vk_prog;
// Initialize binding layout
auto vk_prog = std::make_unique<VKVertexProgram>();
m_vertex_instruction_start = init(vk_prog.get(), compiler_options);
null_prog.ctrl = (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING)
? RSX_SHADER_CONTROL_INSTANCED_CONSTANTS
: 0;
VKVertexDecompilerThread comp(null_prog, shader_str, arr, vk_prog);
VKVertexDecompilerThread comp(null_prog, shader_str, arr, *vk_prog);
// Initialize compiler properties
comp.properties.has_indexed_constants = true;
@ -52,6 +99,12 @@ namespace vk
" uvec4 vp_instructions[];\n"
"};\n\n";
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_VTX_TEXTURES)
{
// FIXME: Unimplemented
rsx_log.todo("Vertex textures are currently not implemented for the shader interpreter.");
}
if (compiler_options & program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING)
{
builder << "#define _ENABLE_INSTANCED_CONSTANTS\n";
@ -68,48 +121,29 @@ namespace vk
builder << program_common::interpreter::get_vertex_interpreter();
const std::string s = builder.str();
auto vs = std::make_unique<glsl::shader>();
auto vs = &vk_prog->shader;
vs->create(::glsl::program_domain::glsl_vertex_program, s);
vs->compile();
// Prepare input table
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
// Declare local inputs
auto vs_inputs = comp.get_inputs();
vk::glsl::program_input in;
in.location = binding_table.vertex_params_bind_slot;
in.set = 0;
in.domain = ::glsl::glsl_vertex_program;
in.name = "VertexContextBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
m_vs_inputs.push_back(in);
in.location = m_vertex_instruction_start;
in.type = glsl::input_type_storage_buffer;
in.name = "VertexInstructionBlock";
vs_inputs.push_back(in);
in.location = binding_table.vertex_buffers_first_bind_slot;
in.name = "persistent_input_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
vk_prog->SetInputs(vs_inputs);
in.location = binding_table.vertex_buffers_first_bind_slot + 1;
in.name = "volatile_input_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_buffers_first_bind_slot + 2;
in.name = "vertex_layout_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_constant_buffers_bind_slot;
in.name = "VertexConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
m_vs_inputs.push_back(in);
// TODO: Bind textures if needed
auto ret = vs.get();
m_shader_cache[compiler_options].m_vs = std::move(vs);
auto ret = vk_prog.get();
m_shader_cache[compiler_options].m_vs = std::move(vk_prog);
return ret;
}
glsl::shader* shader_interpreter::build_fs(u64 compiler_options)
VKFragmentProgram* shader_interpreter::build_fs(u64 compiler_options)
{
[[maybe_unused]] ::glsl::shader_properties properties{};
properties.domain = ::glsl::program_domain::glsl_fragment_program;
@ -120,10 +154,13 @@ namespace vk
ParamArray arr;
std::string shader_str;
RSXFragmentProgram frag;
VKFragmentProgram vk_prog;
VKFragmentDecompilerThread comp(shader_str, arr, frag, len, vk_prog);
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
auto vk_prog = std::make_unique<VKFragmentProgram>();
m_fragment_instruction_start = init(vk_prog.get(), compiler_options);
m_fragment_textures_start = m_fragment_instruction_start + 1;
VKFragmentDecompilerThread comp(shader_str, arr, frag, len, *vk_prog);
std::stringstream builder;
builder <<
"#version 450\n"
@ -199,7 +236,7 @@ namespace vk
for (int i = 0, bind_location = m_fragment_textures_start; i < 4; ++i)
{
builder << "layout(set=0, binding=" << bind_location++ << ") " << "uniform " << type_names[i] << " " << type_names[i] << "_array[16];\n";
builder << "layout(set=1, binding=" << bind_location++ << ") " << "uniform " << type_names[i] << " " << type_names[i] << "_array[16];\n";
}
builder << "\n"
@ -211,7 +248,7 @@ namespace vk
}
builder <<
"layout(std430, binding=" << m_fragment_instruction_start << ") readonly restrict buffer FragmentInstructionBlock\n"
"layout(std430, set=1, binding=" << m_fragment_instruction_start << ") readonly restrict buffer FragmentInstructionBlock\n"
"{\n"
" uint shader_control;\n"
" uint texture_control;\n"
@ -223,140 +260,35 @@ namespace vk
builder << program_common::interpreter::get_fragment_interpreter();
const std::string s = builder.str();
auto fs = std::make_unique<glsl::shader>();
auto fs = &vk_prog->shader;
fs->create(::glsl::program_domain::glsl_fragment_program, s);
fs->compile();
// Prepare input table
// Declare local inputs
auto inputs = comp.get_inputs();
vk::glsl::program_input in;
in.location = binding_table.fragment_constant_buffers_bind_slot;
in.set = 1;
in.domain = ::glsl::glsl_fragment_program;
in.name = "FragmentConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
m_fs_inputs.push_back(in);
in.location = binding_table.fragment_state_bind_slot;
in.name = "FragmentStateBuffer";
m_fs_inputs.push_back(in);
in.location = binding_table.fragment_texture_params_bind_slot;
in.name = "TextureParametersBuffer";
m_fs_inputs.push_back(in);
in.location = m_fragment_instruction_start;
in.type = glsl::input_type_storage_buffer;
in.name = "FragmentInstructionBlock";
inputs.push_back(in);
for (int i = 0, location = m_fragment_textures_start; i < 4; ++i, ++location)
{
in.location = location;
in.name = std::string(type_names[i]) + "_array[16]";
m_fs_inputs.push_back(in);
in.type = glsl::input_type_texture;
inputs.push_back(in);
}
auto ret = fs.get();
m_shader_cache[compiler_options].m_fs = std::move(fs);
vk_prog->SetInputs(inputs);
auto ret = vk_prog.get();
m_shader_cache[compiler_options].m_fs = std::move(vk_prog);
return ret;
}
/*
std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev)
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
auto bindings = get_common_binding_table();
u32 idx = ::size32(bindings);
bindings.resize(binding_table.total_descriptor_bindings);
// Texture 1D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot;
bindings[idx].pImmutableSamplers = nullptr;
m_fragment_textures_start = bindings[idx].binding;
idx++;
// Texture 2D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 1;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Texture 3D array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 2;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Texture CUBE array
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 3;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Vertex texture array (2D only)
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 4;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 4;
bindings[idx].pImmutableSamplers = nullptr;
idx++;
// Vertex program ucode block
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 5;
bindings[idx].pImmutableSamplers = nullptr;
m_vertex_instruction_start = bindings[idx].binding;
idx++;
// Fragment program ucode block
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 6;
bindings[idx].pImmutableSamplers = nullptr;
m_fragment_instruction_start = bindings[idx].binding;
idx++;
bindings.resize(idx);
m_descriptor_pool_sizes = get_descriptor_pool_sizes(bindings);
std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0;
push_constants[0].size = 16;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vk::emulate_conditional_rendering())
{
// Conditional render toggle
push_constants[0].size = 20;
}
const auto set_layout = vk::descriptors::create_layout(bindings);
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants.data();
VkPipelineLayout result;
CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result));
return { set_layout, result };
}
*/
void shader_interpreter::init(const vk::render_device& dev)
{
@ -366,19 +298,14 @@ namespace vk
void shader_interpreter::destroy()
{
m_program_cache.clear();
for (auto &fs : m_shader_cache)
{
fs.second.m_vs->destroy();
fs.second.m_fs->destroy();
}
m_shader_cache.clear();
}
glsl::program* shader_interpreter::link(const vk::pipeline_props& properties, u64 compiler_opt)
{
glsl::shader *fs, *vs;
VKVertexProgram* vs;
VKFragmentProgram* fs;
if (auto found = m_shader_cache.find(compiler_opt); found != m_shader_cache.end())
{
fs = found->second.m_fs.get();
@ -393,12 +320,12 @@ namespace vk
VkPipelineShaderStageCreateInfo shader_stages[2] = {};
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].module = vs->get_handle();
shader_stages[0].module = vs->shader.get_handle();
shader_stages[0].pName = "main";
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = fs->get_handle();
shader_stages[1].module = fs->shader.get_handle();
shader_stages[1].pName = "main";
std::vector<VkDynamicState> dynamic_state_descriptors =
@ -464,7 +391,13 @@ namespace vk
info.renderPass = vk::get_renderpass(m_device, properties.renderpass_key);
auto compiler = vk::get_pipe_compiler();
auto program = compiler->compile(info, vk::pipe_compiler::COMPILE_INLINE, {}, m_vs_inputs, m_fs_inputs);
auto program = compiler->compile(
info,
vk::pipe_compiler::COMPILE_INLINE | vk::pipe_compiler::SEPARATE_SHADER_OBJECTS,
{},
vs->uniforms,
fs->uniforms);
return program.release();
}
@ -486,7 +419,8 @@ namespace vk
glsl::program* shader_interpreter::get(
const vk::pipeline_props& properties,
const program_hash_util::fragment_program_utils::fragment_program_metadata& metadata,
const program_hash_util::fragment_program_utils::fragment_program_metadata& fp_metadata,
const program_hash_util::vertex_program_utils::vertex_program_metadata& vp_metadata,
u32 vp_ctrl,
u32 fp_ctrl)
{
@ -526,11 +460,12 @@ namespace vk
if (fp_ctrl & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_DEPTH_EXPORT;
if (fp_ctrl & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_F32_EXPORT;
if (fp_ctrl & RSX_SHADER_CONTROL_USES_KIL) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_KIL;
if (metadata.referenced_textures_mask) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_TEXTURES;
if (metadata.has_branch_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_FLOW_CTRL;
if (metadata.has_pack_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_PACKING;
if (fp_metadata.referenced_textures_mask) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_TEXTURES;
if (fp_metadata.has_branch_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_FLOW_CTRL;
if (fp_metadata.has_pack_instructions) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_PACKING;
if (rsx::method_registers.polygon_stipple_enabled()) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_STIPPLING;
if (vp_ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_INSTANCING;
if (vp_metadata.referenced_textures_mask) key.compiler_opt |= program_common::interpreter::COMPILER_OPT_ENABLE_VTX_TEXTURES;
if (m_current_key == key) [[likely]]
{
@ -567,4 +502,16 @@ namespace vk
{
return m_fragment_instruction_start;
}
std::pair<VKVertexProgram*, VKFragmentProgram*> shader_interpreter::get_shaders() const
{
if (auto found = m_shader_cache.find(m_current_key.compiler_opt); found != m_shader_cache.end())
{
auto fs = found->second.m_fs.get();
auto vs = found->second.m_vs.get();
return { vs, fs };
}
return { nullptr, nullptr };
}
};

View file

@ -5,6 +5,9 @@
#include "vkutils/descriptors.h"
#include <unordered_map>
class VKVertexProgram;
class VKFragmentProgram;
namespace vk
{
using ::program_hash_util::fragment_program_utils;
@ -12,9 +15,6 @@ namespace vk
class shader_interpreter
{
std::vector<glsl::program_input> m_vs_inputs;
std::vector<glsl::program_input> m_fs_inputs;
VkDevice m_device = VK_NULL_HANDLE;
glsl::program* m_current_interpreter = nullptr;
@ -39,8 +39,8 @@ namespace vk
struct shader_cache_entry_t
{
std::unique_ptr<glsl::shader> m_fs;
std::unique_ptr<glsl::shader> m_vs;
std::unique_ptr<VKFragmentProgram> m_fs;
std::unique_ptr<VKVertexProgram> m_vs;
};
std::unordered_map<pipeline_key, std::unique_ptr<glsl::program>, key_hasher> m_program_cache;
@ -52,20 +52,27 @@ namespace vk
pipeline_key m_current_key{};
glsl::shader* build_vs(u64 compiler_opt);
glsl::shader* build_fs(u64 compiler_opt);
VKVertexProgram* build_vs(u64 compiler_opt);
VKFragmentProgram* build_fs(u64 compiler_opt);
glsl::program* link(const vk::pipeline_props& properties, u64 compiler_opt);
u32 init(VKVertexProgram* vk_prog, u64 compiler_opt) const;
u32 init(VKFragmentProgram* vk_prog, u64 compiler_opt) const;
public:
void init(const vk::render_device& dev);
void destroy();
glsl::program* get(
const vk::pipeline_props& properties,
const program_hash_util::fragment_program_utils::fragment_program_metadata& metadata,
const program_hash_util::fragment_program_utils::fragment_program_metadata& fp_metadata,
const program_hash_util::vertex_program_utils::vertex_program_metadata& vp_metadata,
u32 vp_ctrl,
u32 fp_ctrl);
// Retrieve the shader components that make up the current interpreter
std::pair<VKVertexProgram*, VKFragmentProgram*> get_shaders() const;
bool is_interpreter(const glsl::program* prog) const;
u32 get_vertex_instruction_location() const;

View file

@ -813,7 +813,6 @@ namespace vk
memory_map = vk::get_memory_mapping(pdev);
m_formats_support = vk::get_optimal_tiling_supported_formats(pdev);
m_pipeline_binding_table = vk::get_pipeline_binding_table(pdev);
if (g_cfg.video.disable_vulkan_mem_allocator)
{
@ -1148,15 +1147,4 @@ namespace vk
return result;
}
pipeline_binding_table get_pipeline_binding_table(const vk::physical_device& dev)
{
pipeline_binding_table result{};
// Need to check how many samplers are supported by the driver
const auto usable_samplers = std::min(dev.get_limits().maxPerStageDescriptorSampledImages, 32u);
result.vertex_textures_first_bind_slot = result.textures_first_bind_slot + usable_samplers;
result.total_descriptor_bindings = result.vertex_textures_first_bind_slot + 4;
return result;
}
}

View file

@ -137,7 +137,6 @@ namespace vk
physical_device* pgpu = nullptr;
memory_type_mapping memory_map{};
gpu_formats_support m_formats_support{};
pipeline_binding_table m_pipeline_binding_table{};
std::unique_ptr<mem_allocator_base> m_allocator;
VkDevice dev = VK_NULL_HANDLE;
@ -168,7 +167,6 @@ namespace vk
const physical_device& gpu() const { return *pgpu; }
const memory_type_mapping& get_memory_mapping() const { return memory_map; }
const gpu_formats_support& get_formats_support() const { return m_formats_support; }
const pipeline_binding_table& get_pipeline_binding_table() const { return m_pipeline_binding_table; }
const gpu_shader_types_support& get_shader_types_support() const { return pgpu->shader_types_support; }
const custom_border_color_features& get_custom_border_color_support() const { return pgpu->custom_border_color_support; }
const multidraw_features get_multidraw_support() const { return pgpu->multidraw_support; }
@ -206,7 +204,6 @@ namespace vk
memory_type_mapping get_memory_mapping(const physical_device& dev);
gpu_formats_support get_optimal_tiling_supported_formats(const physical_device& dev);
pipeline_binding_table get_pipeline_binding_table(const physical_device& dev);
extern const render_device* g_render_device;
}