vk: Rewrite program binding management to use "separate shader objects" concept.

This commit is contained in:
kd-11 2025-06-12 03:56:30 +03:00 committed by kd-11
parent 4d493bbb80
commit 356b2f5910
21 changed files with 743 additions and 413 deletions

View file

@ -35,4 +35,34 @@ namespace vk
fmt::throw_exception("Unknown register name: %s", varying_register_name); fmt::throw_exception("Unknown register name: %s", varying_register_name);
} }
int get_texture_index(std::string_view name)
{
if (name.length() < 2)
{
fmt::throw_exception("Invalid texture name: '%s'", name);
}
#define IS_DIGIT(x) (x >= '0' && x <= '9')
constexpr int max_index_length = 2;
std::string index;
for (int char_idx = name.length() - max_index_length; char_idx < name.length(); ++char_idx)
{
if (IS_DIGIT(name[char_idx]))
{
index += name[char_idx];
}
}
#undef IS_DIGIT
if (index.empty())
{
fmt::throw_exception("Invalid texture name: '%s'", name);
}
return std::atoi(index.c_str());
}
} }

View file

@ -6,4 +6,6 @@ namespace vk
using namespace ::glsl; using namespace ::glsl;
int get_varying_register_location(std::string_view varying_register_name); int get_varying_register_location(std::string_view varying_register_name);
int get_texture_index(std::string_view name);
} }

View file

@ -18,6 +18,7 @@ namespace vk
::glsl::glsl_compute_program, ::glsl::glsl_compute_program,
"ssbo" + std::to_string(i), "ssbo" + std::to_string(i),
glsl::program_input_type::input_type_storage_buffer, glsl::program_input_type::input_type_storage_buffer,
0,
i i
); );
result.push_back(input); result.push_back(input);
@ -31,6 +32,7 @@ namespace vk
"push_constants", "push_constants",
glsl::program_input_type::input_type_push_constant, glsl::program_input_type::input_type_push_constant,
0, 0,
0,
glsl::push_constant_ref{ .offset = 0, .size = push_constants_size } glsl::push_constant_ref{ .offset = 0, .size = push_constants_size }
); );
result.push_back(input); result.push_back(input);
@ -243,7 +245,7 @@ namespace vk
void cs_shuffle_base::bind_resources() void cs_shuffle_base::bind_resources()
{ {
m_program->bind_buffer({ m_data->value, m_data_offset, m_data_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ m_data->value, m_data_offset, m_data_length }, 0, 0);
} }
void cs_shuffle_base::set_parameters(const vk::command_buffer& cmd, const u32* params, u8 count) void cs_shuffle_base::set_parameters(const vk::command_buffer& cmd, const u32* params, u8 count)
@ -289,7 +291,7 @@ namespace vk
void cs_interleave_task::bind_resources() void cs_interleave_task::bind_resources()
{ {
m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ m_data->value, m_data_offset, m_ssbo_length }, 0, 0);
} }
void cs_interleave_task::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_offset, u32 data_length, u32 zeta_offset, u32 stencil_offset) void cs_interleave_task::run(const vk::command_buffer& cmd, const vk::buffer* data, u32 data_offset, u32 data_length, u32 zeta_offset, u32 stencil_offset)
@ -349,8 +351,8 @@ namespace vk
void cs_aggregator::bind_resources() void cs_aggregator::bind_resources()
{ {
m_program->bind_buffer({ src->value, 0, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ src->value, 0, block_length }, 0, 0);
m_program->bind_buffer({ dst->value, 0, 4 }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ dst->value, 0, 4 }, 0, 1);
} }
void cs_aggregator::run(const vk::command_buffer& cmd, const vk::buffer* dst, const vk::buffer* src, u32 num_words) void cs_aggregator::run(const vk::command_buffer& cmd, const vk::buffer* dst, const vk::buffer* src, u32 num_words)

View file

@ -344,7 +344,7 @@ namespace vk
void bind_resources() override void bind_resources() override
{ {
m_program->bind_buffer({ m_data->value, m_data_offset, m_ssbo_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ m_data->value, m_data_offset, m_ssbo_length }, 0, 0);
} }
void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 src_offset, u32 src_length, u32 dst_offset) void run(const vk::command_buffer& cmd, const vk::buffer* data, u32 src_offset, u32 src_length, u32 dst_offset)
@ -445,8 +445,8 @@ namespace vk
void bind_resources() override void bind_resources() override
{ {
m_program->bind_buffer({ src_buffer->value, in_offset, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ src_buffer->value, in_offset, block_length }, 0, 0);
m_program->bind_buffer({ dst_buffer->value, out_offset, block_length }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ dst_buffer->value, out_offset, block_length }, 0, 1);
} }
void set_parameters(const vk::command_buffer& cmd) void set_parameters(const vk::command_buffer& cmd)
@ -573,9 +573,9 @@ namespace vk
void bind_resources() override void bind_resources() override
{ {
const auto op = static_cast<int>(Op); const auto op = static_cast<u32>(Op);
m_program->bind_buffer({ src_buffer->value, in_offset, in_block_length }, 0 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ src_buffer->value, in_offset, in_block_length }, 0u, 0u ^ op);
m_program->bind_buffer({ dst_buffer->value, out_offset, out_block_length }, 1 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ dst_buffer->value, out_offset, out_block_length }, 0u, 1u ^ op);
} }
void set_parameters(const vk::command_buffer& cmd) void set_parameters(const vk::command_buffer& cmd)

View file

@ -554,8 +554,8 @@ bool VKGSRender::bind_texture_env()
if (view) [[likely]] if (view) [[likely]]
{ {
m_program->bind_uniform({ fs_sampler_handles[i]->value, view->value, view->image()->current_layout }, m_program->bind_uniform({ fs_sampler_handles[i]->value, view->value, view->image()->current_layout },
i, vk::glsl::binding_set_index_fragment,
::glsl::program_domain::glsl_fragment_program); m_fragment_prog->binding_table.ftex_location[i]);
if (current_fragment_program.texture_state.redirected_textures & (1 << i)) if (current_fragment_program.texture_state.redirected_textures & (1 << i))
{ {
@ -575,24 +575,22 @@ bool VKGSRender::bind_texture_env()
} }
m_program->bind_uniform({ m_stencil_mirror_sampler->value, stencil_view->value, stencil_view->image()->current_layout }, m_program->bind_uniform({ m_stencil_mirror_sampler->value, stencil_view->value, stencil_view->image()->current_layout },
i, vk::glsl::binding_set_index_fragment,
::glsl::program_domain::glsl_fragment_program, m_fragment_prog->binding_table.ftex_stencil_location[i]);
true);
} }
} }
else else
{ {
const VkImageViewType view_type = vk::get_view_type(current_fragment_program.get_texture_dimension(i)); 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 }, 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 },
i, vk::glsl::binding_set_index_fragment,
::glsl::program_domain::glsl_fragment_program); m_fragment_prog->binding_table.ftex_location[i]);
if (current_fragment_program.texture_state.redirected_textures & (1 << 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 }, 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 },
i, vk::glsl::binding_set_index_fragment,
::glsl::program_domain::glsl_fragment_program, m_fragment_prog->binding_table.ftex_stencil_location[i]);
true);
} }
} }
} }
@ -606,8 +604,8 @@ bool VKGSRender::bind_texture_env()
{ {
const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i)); 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 }, 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 },
i, vk::glsl::binding_set_index_vertex,
::glsl::program_domain::glsl_vertex_program); m_vertex_prog->binding_table.vtex_location[i]);
continue; continue;
} }
@ -629,8 +627,8 @@ bool VKGSRender::bind_texture_env()
const auto view_type = vk::get_view_type(current_vertex_program.get_texture_dimension(i)); 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 }, 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 },
i, vk::glsl::binding_set_index_vertex,
::glsl::program_domain::glsl_vertex_program); m_vertex_prog->binding_table.vtex_location[i]);
continue; continue;
} }
@ -638,8 +636,8 @@ bool VKGSRender::bind_texture_env()
validate_image_layout_for_read_access(*m_current_command_buffer, image_ptr, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, sampler_state); validate_image_layout_for_read_access(*m_current_command_buffer, image_ptr, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, sampler_state);
m_program->bind_uniform({ vs_sampler_handles[i]->value, image_ptr->value, image_ptr->image()->current_layout }, m_program->bind_uniform({ vs_sampler_handles[i]->value, image_ptr->value, image_ptr->image()->current_layout },
i, vk::glsl::binding_set_index_vertex,
::glsl::program_domain::glsl_vertex_program); m_vertex_prog->binding_table.vtex_location[i]);
} }
return out_of_memory; return out_of_memory;
@ -820,8 +818,6 @@ void VKGSRender::emit_geometry(u32 sub_index)
auto volatile_buffer = m_volatile_attribute_storage ? m_volatile_attribute_storage->value : null_buffer_view->value; auto volatile_buffer = m_volatile_attribute_storage ? m_volatile_attribute_storage->value : null_buffer_view->value;
bool update_descriptors = false; bool update_descriptors = false;
const auto& binding_table = m_device->get_pipeline_binding_table();
if (m_current_draw.subdraw_id == 0) if (m_current_draw.subdraw_id == 0)
{ {
update_descriptors = true; update_descriptors = true;
@ -878,9 +874,10 @@ void VKGSRender::emit_geometry(u32 sub_index)
ensure(m_vertex_layout_storage); ensure(m_vertex_layout_storage);
if (update_descriptors) if (update_descriptors)
{ {
m_program->bind_uniform(persistent_buffer, binding_table.vertex_buffers_first_bind_slot); const auto& binding_table = m_vertex_prog->binding_table;
m_program->bind_uniform(volatile_buffer, binding_table.vertex_buffers_first_bind_slot + 1); m_program->bind_uniform(persistent_buffer, vk::glsl::binding_set_index_vertex, binding_table.vertex_buffers_location);
m_program->bind_uniform(m_vertex_layout_storage->value, binding_table.vertex_buffers_first_bind_slot + 2); 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);
} }
bool reload_state = (!m_current_draw.subdraw_id++); bool reload_state = (!m_current_draw.subdraw_id++);

View file

@ -26,8 +26,85 @@ std::string VKFragmentDecompilerThread::compareFunction(COMPARE f, const std::st
return glsl::compareFunctionImpl(f, Op0, Op1); return glsl::compareFunctionImpl(f, Op0, Op1);
} }
void VKFragmentDecompilerThread::prepareBindingTable()
{
// First check if we have constants and textures as those need extra work
bool has_constants = false, has_textures = false;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
if (has_constants && has_textures)
{
break;
}
if (PT.type.starts_with("sampler"))
{
has_textures = true;
continue;
}
ensure(PT.type.starts_with("vec"));
has_constants = true;
}
unsigned location = 0; // All bindings must be set from this var
vk_prog->binding_table.context_buffer_location = location++;
if (has_constants)
{
vk_prog->binding_table.cbuf_location = location++;
}
vk_prog->binding_table.tex_param_location = location++;
vk_prog->binding_table.polygon_stipple_params_location = location++;
if (has_textures) [[ likely ]]
{
unsigned num_textures = 0;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
if (!PT.type.starts_with("sampler"))
{
continue;
}
for (const ParamItem& PI : PT.items)
{
num_textures++;
const auto texture_id = vk::get_texture_index(PI.name);
const auto mask = 1u << texture_id;
// Allocate real binding
vk_prog->binding_table.ftex_location[texture_id] = location++;
// Tag the stencil mirror if required
if (properties.redirected_sampler_mask & mask) [[ unlikely ]]
{
vk_prog->binding_table.ftex_stencil_location[texture_id] = 0;
}
}
// Normalize stencil offsets
if (properties.redirected_sampler_mask != 0) [[ unlikely ]]
{
for (auto& stencil_location : vk_prog->binding_table.ftex_stencil_location)
{
if (stencil_location == umax)
{
continue;
}
stencil_location = location++;
}
}
}
}
}
void VKFragmentDecompilerThread::insertHeader(std::stringstream & OS) void VKFragmentDecompilerThread::insertHeader(std::stringstream & OS)
{ {
prepareBindingTable();
std::vector<const char*> required_extensions; std::vector<const char*> required_extensions;
if (device_props.has_native_half_support) if (device_props.has_native_half_support)
@ -97,21 +174,18 @@ void VKFragmentDecompilerThread::insertOutputs(std::stringstream & OS)
void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS) void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
{ {
u32 location = m_binding_table.textures_first_bind_slot;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM]) for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{ {
if (PT.type != "sampler1D" && if (PT.type.starts_with("sampler1D"))
PT.type != "sampler2D" && {
PT.type != "sampler3D" &&
PT.type != "samplerCube")
continue; continue;
}
for (const ParamItem& PI : PT.items) for (const ParamItem& PI : PT.items)
{ {
std::string samplerType = PT.type; std::string samplerType = PT.type;
ensure(PI.name.length() > 3); const int index = vk::get_texture_index(PI.name);
int index = atoi(&PI.name[3]);
const auto mask = (1 << index); const auto mask = (1 << index);
if (properties.multisampled_sampler_mask & mask) if (properties.multisampled_sampler_mask & mask)
@ -135,39 +209,37 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
} }
} }
vk::glsl::program_input in; const int id = vk::get_texture_index(PI.name);
in.location = location; auto in = vk::glsl::program_input::make(
in.domain = glsl::glsl_fragment_program; glsl::glsl_fragment_program,
in.name = PI.name; PI.name,
in.type = vk::glsl::input_type_texture; vk::glsl::input_type_texture,
vk::glsl::binding_set_index_fragment,
vk_prog->binding_table.ftex_location[id]
);
inputs.push_back(in); inputs.push_back(in);
OS << "layout(set=0, binding=" << location++ << ") uniform " << samplerType << " " << PI.name << ";\n"; OS << "layout(set=0, binding=" << in.location << ") uniform " << samplerType << " " << PI.name << ";\n";
if (properties.redirected_sampler_mask & mask) if (properties.redirected_sampler_mask & mask)
{ {
// Insert stencil mirror declaration // Insert stencil mirror declaration
in.name += "_stencil"; in.name += "_stencil";
in.location = location; in.location = vk_prog->binding_table.ftex_stencil_location[id];
inputs.push_back(in); inputs.push_back(in);
OS << "layout(set=0, binding=" << location++ << ") uniform u" << samplerType << " " << in.name << ";\n"; OS << "layout(set=0, binding=" << in.location << ") uniform u" << samplerType << " " << in.name << ";\n";
} }
} }
} }
ensure(location <= m_binding_table.vertex_textures_first_bind_slot); // "Too many sampler descriptors!"
std::string constants_block; std::string constants_block;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM]) for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{ {
if (PT.type == "sampler1D" || if (PT.type.starts_with("sampler1D"))
PT.type == "sampler2D" || {
PT.type == "sampler3D" ||
PT.type == "samplerCube")
continue; continue;
}
for (const ParamItem& PI : PT.items) for (const ParamItem& PI : PT.items)
{ {
@ -177,13 +249,13 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
if (!constants_block.empty()) if (!constants_block.empty())
{ {
OS << "layout(std140, set = 0, binding = 2) uniform FragmentConstantsBuffer\n"; OS << "layout(std140, set = 1, binding = " << vk_prog->binding_table.cbuf_location << ") uniform FragmentConstantsBuffer\n";
OS << "{\n"; OS << "{\n";
OS << constants_block; OS << constants_block;
OS << "};\n\n"; OS << "};\n\n";
} }
OS << "layout(std140, set = 0, binding = 3) uniform FragmentStateBuffer\n"; OS << "layout(std140, set = 1, binding = " << vk_prog->binding_table.context_buffer_location << ") uniform FragmentStateBuffer\n";
OS << "{\n"; OS << "{\n";
OS << " float fog_param0;\n"; OS << " float fog_param0;\n";
OS << " float fog_param1;\n"; OS << " float fog_param1;\n";
@ -195,32 +267,39 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
OS << " float wpos_bias;\n"; OS << " float wpos_bias;\n";
OS << "};\n\n"; OS << "};\n\n";
OS << "layout(std140, set = 0, binding = 4) uniform TextureParametersBuffer\n"; OS << "layout(std140, set = 1, binding = " << vk_prog->binding_table.tex_param_location << ") uniform TextureParametersBuffer\n";
OS << "{\n"; OS << "{\n";
OS << " sampler_info texture_parameters[16];\n"; OS << " sampler_info texture_parameters[16];\n";
OS << "};\n\n"; OS << "};\n\n";
OS << "layout(std140, set = 0, binding = " << std::to_string(m_binding_table.rasterizer_env_bind_slot) << ") uniform RasterizerHeap\n"; OS << "layout(std140, set = 1, binding = " << vk_prog->binding_table.polygon_stipple_params_location << ") uniform RasterizerHeap\n";
OS << "{\n"; OS << "{\n";
OS << " uvec4 stipple_pattern[8];\n"; OS << " uvec4 stipple_pattern[8];\n";
OS << "};\n\n"; OS << "};\n\n";
vk::glsl::program_input in; vk::glsl::program_input in
in.location = m_binding_table.fragment_constant_buffers_bind_slot; {
in.domain = glsl::glsl_fragment_program; .domain = glsl::glsl_fragment_program,
in.name = "FragmentConstantsBuffer"; .type = vk::glsl::input_type_uniform_buffer,
in.type = vk::glsl::input_type_uniform_buffer; .set = vk::glsl::binding_set_index_fragment
inputs.push_back(in); };
in.location = m_binding_table.fragment_state_bind_slot; if (!constants_block.empty())
{
in.location = vk_prog->binding_table.cbuf_location;
in.name = "FragmentConstantsBuffer";
inputs.push_back(in);
}
in.location = vk_prog->binding_table.context_buffer_location;
in.name = "FragmentStateBuffer"; in.name = "FragmentStateBuffer";
inputs.push_back(in); inputs.push_back(in);
in.location = m_binding_table.fragment_texture_params_bind_slot; in.location = vk_prog->binding_table.tex_param_location;
in.name = "TextureParametersBuffer"; in.name = "TextureParametersBuffer";
inputs.push_back(in); inputs.push_back(in);
in.location = m_binding_table.rasterizer_env_bind_slot; in.location = vk_prog->binding_table.polygon_stipple_params_location;
in.name = "RasterizerHeap"; in.name = "RasterizerHeap";
inputs.push_back(in); inputs.push_back(in);
} }
@ -372,7 +451,6 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
void VKFragmentDecompilerThread::Task() void VKFragmentDecompilerThread::Task()
{ {
m_binding_table = vk::g_render_device->get_pipeline_binding_table();
m_shader = Decompile(); m_shader = Decompile();
vk_prog->SetInputs(inputs); vk_prog->SetInputs(inputs);
} }

View file

@ -19,7 +19,8 @@ struct VKFragmentDecompilerThread : public FragmentProgramDecompiler
std::vector<vk::glsl::program_input> inputs; std::vector<vk::glsl::program_input> inputs;
class VKFragmentProgram *vk_prog; class VKFragmentProgram *vk_prog;
glsl::shader_properties m_shader_props{}; glsl::shader_properties m_shader_props{};
vk::pipeline_binding_table m_binding_table{};
void prepareBindingTable();
public: public:
VKFragmentDecompilerThread(std::string& shader, ParamArray& parr, const RSXFragmentProgram &prog, u32& size, class VKFragmentProgram& dst) VKFragmentDecompilerThread(std::string& shader, ParamArray& parr, const RSXFragmentProgram &prog, u32& size, class VKFragmentProgram& dst)
@ -32,6 +33,7 @@ public:
void Task(); void Task();
const std::vector<vk::glsl::program_input>& get_inputs() { return inputs; } const std::vector<vk::glsl::program_input>& get_inputs() { return inputs; }
protected: protected:
std::string getFloatTypeName(usz elementCount) override; std::string getFloatTypeName(usz elementCount) override;
std::string getHalfTypeName(usz elementCount) override; std::string getHalfTypeName(usz elementCount) override;
@ -63,8 +65,19 @@ public:
std::vector<usz> FragmentConstantOffsetCache; std::vector<usz> FragmentConstantOffsetCache;
std::array<u32, 4> output_color_masks{ {} }; std::array<u32, 4> output_color_masks{ {} };
std::vector<vk::glsl::program_input> uniforms; std::vector<vk::glsl::program_input> uniforms;
struct
{
u32 context_buffer_location = umax; // Rasterizer context
u32 cbuf_location = umax; // Constants register file
u32 tex_param_location = umax; // Texture configuration data
u32 polygon_stipple_params_location = umax; // Polygon stipple settings
u32 ftex_location[16]; // Texture locations array
u32 ftex_stencil_location[16]; // Texture stencil mirror array
} binding_table;
void SetInputs(std::vector<vk::glsl::program_input>& inputs); void SetInputs(std::vector<vk::glsl::program_input>& inputs);
/** /**
* Decompile a fragment shader located in the PS3's Memory. This function operates synchronously. * Decompile a fragment shader located in the PS3's Memory. This function operates synchronously.

View file

@ -2074,34 +2074,35 @@ void VKGSRender::load_program_env()
} }
} }
const auto& binding_table = m_device->get_pipeline_binding_table(); 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, binding_table.vertex_params_bind_slot); m_program->bind_uniform(m_vertex_env_buffer_info, vk::glsl::binding_set_index_vertex, vs_binding_table.context_buffer_location);
m_program->bind_buffer(m_vertex_constants_buffer_info, binding_table.vertex_constant_buffers_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); 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_fragment_env_buffer_info, binding_table.fragment_state_bind_slot); 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, binding_table.fragment_texture_params_bind_slot); 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, binding_table.rasterizer_env_bind_slot); m_program->bind_uniform(m_raster_env_buffer_info, vk::glsl::binding_set_index_fragment, fs_binding_table.polygon_stipple_params_location);
if (!m_shader_interpreter.is_interpreter(m_program)) if (m_shader_interpreter.is_interpreter(m_program))
{ {
m_program->bind_uniform(m_fragment_constants_buffer_info, binding_table.fragment_constant_buffers_bind_slot); 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 else if (fs_binding_table.cbuf_location != umax)
{ {
m_program->bind_buffer(m_vertex_instructions_buffer_info, m_shader_interpreter.get_vertex_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform(m_fragment_constants_buffer_info, vk::glsl::binding_set_index_fragment, fs_binding_table.cbuf_location);
m_program->bind_buffer(m_fragment_instructions_buffer_info, m_shader_interpreter.get_fragment_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
} }
if (vk::emulate_conditional_rendering()) 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; auto predicate = m_cond_render_buffer ? m_cond_render_buffer->value : vk::get_scratch_buffer(*m_current_command_buffer, 4)->value;
m_program->bind_buffer({ predicate, 0, 4 }, binding_table.conditional_render_predicate_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform({ predicate, 0, 4 }, vk::glsl::binding_set_index_vertex, vs_binding_table.cr_pred_buffer_location);
} }
if (current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS) if (current_vertex_program.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS)
{ {
m_program->bind_buffer(m_instancing_indirection_buffer_info, binding_table.instancing_lookup_table_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); 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_buffer(m_instancing_constants_array_buffer_info, binding_table.instancing_constants_buffer_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); m_program->bind_uniform(m_instancing_constants_array_buffer_info, vk::glsl::binding_set_index_vertex, vs_binding_table.instanced_cbuf_location);
} }
// Clear flags // Clear flags

View file

@ -63,21 +63,21 @@ namespace vk
for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding) for (u32 n = 0; n < m_num_uniform_buffers; ++n, ++binding)
{ {
const std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : ""); const std::string name = std::string("static_data") + (n > 0 ? std::to_string(n) : "");
const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_uniform_buffer, 0); const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_uniform_buffer, 0, 0);
fs_inputs.push_back(input); fs_inputs.push_back(input);
} }
for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding) for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding)
{ {
const std::string name = "fs" + std::to_string(n); const std::string name = "fs" + std::to_string(n);
const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, binding); const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, 0, binding);
fs_inputs.push_back(input); fs_inputs.push_back(input);
} }
for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding) for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding)
{ {
const std::string name = "sp" + std::to_string(n); const std::string name = "sp" + std::to_string(n);
const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, binding); const auto input = program_input::make(::glsl::program_domain::glsl_fragment_program, name, program_input_type::input_type_texture, 0, binding);
fs_inputs.push_back(input); fs_inputs.push_back(input);
} }
@ -179,13 +179,14 @@ namespace vk
if (m_num_uniform_buffers > 0) if (m_num_uniform_buffers > 0)
{ {
program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0); program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0, 0);
} }
for (uint n = 0; n < src.size(); ++n) for (uint n = 0; n < src.size(); ++n)
{ {
VkDescriptorImageInfo info = { m_sampler->value, src[n]->value, src[n]->image()->current_layout }; VkDescriptorImageInfo info = { m_sampler->value, src[n]->value, src[n]->image()->current_layout };
program->bind_uniform(info, "fs" + std::to_string(n), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); const auto [set, location] = program->get_uniform_location(::glsl::glsl_fragment_program, glsl::input_type_texture, "fs" + std::to_string(n));
program->bind_uniform(info, set, location);
} }
program->bind(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS); program->bind(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS);
@ -488,6 +489,7 @@ namespace vk
"push_constants", "push_constants",
glsl::input_type_push_constant, glsl::input_type_push_constant,
0, 0,
0,
glsl::push_constant_ref { .size = 68 } glsl::push_constant_ref { .size = 68 }
) )
); );
@ -503,6 +505,7 @@ namespace vk
"push_constants", "push_constants",
glsl::input_type_push_constant, glsl::input_type_push_constant,
0, 0,
0,
glsl::push_constant_ref {.offset = 68, .size = 12 } glsl::push_constant_ref {.offset = 68, .size = 12 }
) )
); );
@ -715,6 +718,7 @@ namespace vk
"push_constants", "push_constants",
vk::glsl::input_type_push_constant, vk::glsl::input_type_push_constant,
0, 0,
0,
glsl::push_constant_ref{ .size = 32 }) glsl::push_constant_ref{ .size = 32 })
}; };
} }
@ -864,6 +868,7 @@ namespace vk
"push_constants", "push_constants",
vk::glsl::input_type_push_constant, vk::glsl::input_type_push_constant,
0, 0,
0,
glsl::push_constant_ref{ .size = 16 } glsl::push_constant_ref{ .size = 16 }
) )
); );

View file

@ -54,7 +54,9 @@ namespace vk
const VkComputePipelineCreateInfo& create_info, const VkComputePipelineCreateInfo& create_info,
const std::vector<glsl::program_input>& cs_inputs) const std::vector<glsl::program_input>& cs_inputs)
{ {
return std::make_unique<glsl::program>(*m_device, create_info, cs_inputs); auto program = std::make_unique<glsl::program>(*m_device, create_info, cs_inputs);
program->link(false);
return program;
} }
std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe( std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe(
@ -62,7 +64,9 @@ namespace vk
const std::vector<glsl::program_input>& vs_inputs, const std::vector<glsl::program_input>& vs_inputs,
const std::vector<glsl::program_input>& fs_inputs) const std::vector<glsl::program_input>& fs_inputs)
{ {
return std::make_unique<glsl::program>(*m_device, create_info, vs_inputs, fs_inputs); auto program = std::make_unique<glsl::program>(*m_device, create_info, vs_inputs, fs_inputs);
program->link(true);
return program;
} }
std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe( std::unique_ptr<glsl::program> pipe_compiler::int_compile_graphics_pipe(
@ -171,7 +175,7 @@ namespace vk
op_flags flags, callback_t callback, op_flags flags, callback_t callback,
const std::vector<glsl::program_input>& cs_inputs) const std::vector<glsl::program_input>& cs_inputs)
{ {
if (flags == COMPILE_INLINE) if (flags & COMPILE_INLINE)
{ {
return int_compile_compute_pipe(create_info, cs_inputs); return int_compile_compute_pipe(create_info, cs_inputs);
} }
@ -187,7 +191,7 @@ namespace vk
const std::vector<glsl::program_input>& fs_inputs) const std::vector<glsl::program_input>& fs_inputs)
{ {
// It is very inefficient to defer this as all pointers need to be saved // It is very inefficient to defer this as all pointers need to be saved
ensure(flags == COMPILE_INLINE); ensure(flags & COMPILE_INLINE);
return int_compile_graphics_pipe(create_info, vs_inputs, fs_inputs); return int_compile_graphics_pipe(create_info, vs_inputs, fs_inputs);
} }
@ -200,7 +204,7 @@ namespace vk
const std::vector<glsl::program_input>& fs_inputs) const std::vector<glsl::program_input>& fs_inputs)
{ {
VkShaderModule modules[] = { vs, fs }; VkShaderModule modules[] = { vs, fs };
if (flags == COMPILE_INLINE) if (flags & COMPILE_INLINE)
{ {
return int_compile_graphics_pipe(create_info, modules, vs_inputs, fs_inputs); return int_compile_graphics_pipe(create_info, modules, vs_inputs, fs_inputs);
} }

View file

@ -53,13 +53,16 @@ namespace vk
class pipe_compiler class pipe_compiler
{ {
public: public:
enum op_flags enum op_flag_bits
{ {
COMPILE_DEFAULT = 0, COMPILE_DEFAULT = 0,
COMPILE_INLINE = 1, COMPILE_INLINE = 1,
COMPILE_DEFERRED = 2 COMPILE_DEFERRED = 2,
SEPARATE_SHADER_OBJECTS = 4
}; };
using op_flags = rsx::flags32_t;
using callback_t = std::function<void(std::unique_ptr<glsl::program>&)>; using callback_t = std::function<void(std::unique_ptr<glsl::program>&)>;
pipe_compiler(); pipe_compiler();

View file

@ -0,0 +1,12 @@
#pragma once
#include "VKProgramPipeline.h"
namespace vk
{
namespace glsl
{
}
}

View file

@ -14,6 +14,30 @@ namespace vk
{ {
using namespace ::glsl; using namespace ::glsl;
bool operator == (const descriptor_slot_t& a, const VkDescriptorImageInfo& b)
{
const auto ptr = std::get_if<VkDescriptorImageInfo>(&a);
return !!ptr &&
ptr->imageView == b.imageView &&
ptr->sampler == b.sampler &&
ptr->imageLayout == b.imageLayout;
}
bool operator == (const descriptor_slot_t& a, const VkDescriptorBufferInfo& b)
{
const auto ptr = std::get_if<VkDescriptorBufferInfo>(&a);
return !!ptr &&
ptr->buffer == b.buffer &&
ptr->offset == b.offset &&
ptr->range == b.range;
}
bool operator == (const descriptor_slot_t& a, const VkBufferView& b)
{
const auto ptr = std::get_if<VkBufferView>(&a);
return !!ptr && *ptr == b;
}
VkDescriptorType to_descriptor_type(program_input_type type) VkDescriptorType to_descriptor_type(program_input_type type)
{ {
switch (type) switch (type)
@ -120,42 +144,24 @@ namespace vk
void program::init() void program::init()
{ {
linked = false; m_linked = false;
fs_texture_bindings.fill(~0u);
fs_texture_mirror_bindings.fill(~0u);
vs_texture_bindings.fill(~0u);
} }
program::program(VkDevice dev, const VkGraphicsPipelineCreateInfo& create_info, const std::vector<program_input> &vertex_inputs, const std::vector<program_input>& fragment_inputs) program::program(VkDevice dev, const VkGraphicsPipelineCreateInfo& create_info, const std::vector<program_input> &vertex_inputs, const std::vector<program_input>& fragment_inputs)
: m_device(dev) : m_device(dev), m_info(create_info)
{ {
init(); init();
load_uniforms(vertex_inputs); load_uniforms(vertex_inputs);
load_uniforms(fragment_inputs); load_uniforms(fragment_inputs);
create_pipeline_layout();
ensure(m_pipeline_layout);
auto _create_info = create_info;
_create_info.layout = m_pipeline_layout;
CHECK_RESULT(vkCreateGraphicsPipelines(dev, nullptr, 1, &create_info, nullptr, &m_pipeline));
} }
program::program(VkDevice dev, const VkComputePipelineCreateInfo& create_info, const std::vector<program_input>& compute_inputs) program::program(VkDevice dev, const VkComputePipelineCreateInfo& create_info, const std::vector<program_input>& compute_inputs)
: m_device(dev) : m_device(dev), m_info(create_info)
{ {
init(); init();
load_uniforms(compute_inputs); load_uniforms(compute_inputs);
create_pipeline_layout();
ensure(m_pipeline_layout);
auto _create_info = create_info;
_create_info.layout = m_pipeline_layout;
CHECK_RESULT(vkCreateComputePipelines(dev, nullptr, 1, &create_info, nullptr, &m_pipeline));
} }
program::~program() program::~program()
@ -165,257 +171,352 @@ namespace vk
if (m_pipeline_layout) if (m_pipeline_layout)
{ {
vkDestroyPipelineLayout(m_device, m_pipeline_layout, nullptr); vkDestroyPipelineLayout(m_device, m_pipeline_layout, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_descriptor_set_layout, nullptr);
vk::get_resource_manager()->dispose(m_descriptor_pool); for (auto& set : m_sets)
{
set.destroy();
}
} }
} }
program& program::load_uniforms(const std::vector<program_input>& inputs) program& program::load_uniforms(const std::vector<program_input>& inputs)
{ {
ensure(!linked); // "Cannot change uniforms in already linked program!" ensure(!m_linked); // "Cannot change uniforms in already linked program!"
for (auto &item : inputs) for (auto &item : inputs)
{ {
uniforms[item.type].push_back(item); ensure(item.set < binding_set_index_max_enum); // Ensure we have a valid set id
ensure(item.location < 128u || item.type == input_type_push_constant); // Arbitrary limit but useful to catch possibly uninitialized values
m_sets[item.set].m_inputs[item.type].push_back(item);
} }
return *this; return *this;
} }
program& program::link() program& program::link(bool separate_objects)
{ {
// Preprocess texture bindings auto p_graphics_info = std::get_if<VkGraphicsPipelineCreateInfo>(&m_info);
// Link step is only useful for rasterizer programs, compute programs do not need this auto p_compute_info = !p_graphics_info ? std::get_if<VkComputePipelineCreateInfo>(&m_info) : nullptr;
for (const auto &uniform : uniforms[program_input_type::input_type_texture]) const bool is_graphics_pipe = p_graphics_info != nullptr;
{
if (const auto name_start = uniform.name.find("tex"); name_start != umax)
{
const auto name_end = uniform.name.find("_stencil");
const auto index_start = name_start + 3; // Skip 'tex' part
const auto index_length = (name_end != umax) ? name_end - index_start : name_end;
const auto index_part = uniform.name.substr(index_start, index_length);
const auto index = std::stoi(index_part);
if (name_start == 0) if (!is_graphics_pipe) [[ likely ]]
{
// We only support compute and graphics, so disable this for compute
separate_objects = false;
}
if (!separate_objects)
{
// Collapse all sets into set 0 if validation passed
auto& sink = m_sets[0];
for (auto& set : m_sets)
{
for (auto& type_arr : set.m_inputs)
{ {
// Fragment texture (tex...) if (type_arr.empty())
if (name_end == umax)
{ {
// Normal texture continue;
fs_texture_bindings[index] = uniform.location;
}
else
{
// Stencil mirror
fs_texture_mirror_bindings[index] = uniform.location;
} }
auto type = type_arr.front().type;
auto& dst = sink.m_inputs[type];
dst.insert(dst.end(), type_arr.begin(), type_arr.end());
// Clear
type_arr.clear();
} }
else }
sink.validate();
sink.init(m_device);
}
else
{
for (auto& set : m_sets)
{
for (auto& type_arr : set.m_inputs)
{ {
// Vertex texture (vtex...) if (type_arr.empty())
vs_texture_bindings[index] = uniform.location; {
continue;
}
// Real set
set.validate();
set.init(m_device);
break;
} }
} }
} }
linked = true; create_pipeline_layout();
ensure(m_pipeline_layout);
if (is_graphics_pipe)
{
VkGraphicsPipelineCreateInfo create_info = *p_graphics_info;
create_info.layout = m_pipeline_layout;
CHECK_RESULT(vkCreateGraphicsPipelines(m_device, nullptr, 1, &create_info, nullptr, &m_pipeline));
}
else
{
VkComputePipelineCreateInfo create_info = *p_compute_info;
create_info.layout = m_pipeline_layout;
CHECK_RESULT(vkCreateComputePipelines(m_device, nullptr, 1, &create_info, nullptr, &m_pipeline));
}
m_linked = true;
return *this; return *this;
} }
bool program::has_uniform(program_input_type type, const std::string& uniform_name) bool program::has_uniform(program_input_type type, const std::string& uniform_name)
{ {
const auto& uniform = uniforms[type]; for (auto& set : m_sets)
return std::any_of(uniform.cbegin(), uniform.cend(), [&uniform_name](const auto& u)
{ {
return u.name == uniform_name; const auto& uniform = set.m_inputs[type];
}); return std::any_of(uniform.cbegin(), uniform.cend(), [&uniform_name](const auto& u)
}
u32 program::get_uniform_location(program_input_type type, const std::string& uniform_name)
{
const auto& uniform = uniforms[type];
const auto result = std::find_if(uniform.cbegin(), uniform.cend(), [&uniform_name](const auto& u)
{
return u.name == uniform_name;
});
if (result == uniform.end())
{
return { umax };
}
return result->location;
}
void program::bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string& uniform_name, VkDescriptorType type)
{
for (const auto &uniform : uniforms[program_input_type::input_type_texture])
{
if (uniform.name == uniform_name)
{ {
if (m_descriptor_slots[uniform.location].matches(image_descriptor)) return u.name == uniform_name;
{ });
return; }
} }
next_descriptor_set(); std::pair<u32, u32> program::get_uniform_location(::glsl::program_domain domain, program_input_type type, const std::string& uniform_name)
m_descriptor_set.push(image_descriptor, type, uniform.location); {
m_descriptors_dirty[uniform.location] = false; for (unsigned i = 0; i < ::size32(m_sets); ++i)
return; {
const auto& type_arr = m_sets[i].m_inputs[type];
const auto result = std::find_if(type_arr.cbegin(), type_arr.cend(), [&](const auto& u)
{
return u.domain == domain && u.name == uniform_name;
});
if (result != type_arr.end())
{
return { i, result->location };
} }
} }
rsx_log.notice("texture not found in program: %s", uniform_name.c_str()); return { umax, umax };
} }
void program::bind_uniform(const VkDescriptorImageInfo & image_descriptor, int texture_unit, ::glsl::program_domain domain, bool is_stencil_mirror) void program::bind_uniform(const VkDescriptorImageInfo& image_descriptor, u32 set_id, u32 binding_point)
{ {
ensure(domain != ::glsl::program_domain::glsl_compute_program); if (m_sets[set_id].m_descriptor_slots[binding_point] == image_descriptor)
u32 binding;
if (domain == ::glsl::program_domain::glsl_fragment_program)
{
binding = (is_stencil_mirror) ? fs_texture_mirror_bindings[texture_unit] : fs_texture_bindings[texture_unit];
}
else
{
binding = vs_texture_bindings[texture_unit];
}
if (binding == ~0u) [[ unlikely ]]
{
rsx_log.notice("texture not found in program: %stex%u", (domain == ::glsl::program_domain::glsl_vertex_program) ? "v" : "", texture_unit);
return;
}
if (m_descriptor_slots[binding].matches(image_descriptor))
{ {
return; return;
} }
next_descriptor_set(); m_sets[set_id].notify_descriptor_slot_updated(binding_point, image_descriptor);
m_descriptor_set.push(image_descriptor, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, binding);
m_descriptors_dirty[binding] = false;
} }
void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point) void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 set_id, u32 binding_point)
{ {
bind_buffer(buffer_descriptor, binding_point, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); if (m_sets[set_id].m_descriptor_slots[binding_point] == buffer_descriptor)
}
void program::bind_uniform(const VkBufferView &buffer_view, u32 binding_point)
{
if (m_descriptor_slots[binding_point].matches(buffer_view))
{ {
return; return;
} }
next_descriptor_set(); m_sets[set_id].notify_descriptor_slot_updated(binding_point, buffer_descriptor);
m_descriptor_set.push(buffer_view, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, binding_point);
m_descriptors_dirty[binding_point] = false;
} }
void program::bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name) void program::bind_uniform(const VkBufferView &buffer_view, u32 set_id, u32 binding_point)
{ {
for (const auto &uniform : uniforms[type]) if (m_sets[set_id].m_descriptor_slots[binding_point] == buffer_view)
{
if (uniform.name == binding_name)
{
bind_uniform(buffer_view, uniform.location);
return;
}
}
rsx_log.notice("vertex buffer not found in program: %s", binding_name.c_str());
}
void program::bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type)
{
if (m_descriptor_slots[binding_point].matches(buffer_descriptor))
{ {
return; return;
} }
next_descriptor_set(); m_sets[set_id].notify_descriptor_slot_updated(binding_point, buffer_view);
m_descriptor_set.push(buffer_descriptor, type, binding_point);
m_descriptors_dirty[binding_point] = false;
} }
void program::bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, VkDescriptorType type, int count, u32 binding_point) void program::bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, VkDescriptorType type, int count, u32 set_id, u32 binding_point)
{ {
// FIXME: Unoptimized... auto& set = m_sets[set_id];
bool match = true;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
if (!m_descriptor_slots[binding_point + i].matches(image_descriptors[i])) if (set.m_descriptor_slots[binding_point + i] != image_descriptors[i])
{
set.notify_descriptor_slot_updated(binding_point + i, image_descriptors[i]);
}
}
}
void program::create_pipeline_layout()
{
ensure(!m_linked);
ensure(m_pipeline_layout == VK_NULL_HANDLE);
rsx::simple_array<VkPushConstantRange> push_constants{};
rsx::simple_array<VkDescriptorSetLayout> set_layouts{};
for (auto& set : m_sets)
{
if (!set.m_device)
{ {
match = false;
break; break;
} }
set.next_descriptor_set(); // Initializes the set layout and allocates first set
set_layouts.push_back(set.m_descriptor_set_layout);
for (const auto& input : set.m_inputs[input_type_push_constant])
{
const auto& range = input.as_push_constant();
push_constants.push_back({
.stageFlags = to_shader_stage_flags(input.domain),
.offset = range.offset,
.size = range.size
});
}
} }
if (match) VkPipelineLayoutCreateInfo create_info
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.flags = 0,
.setLayoutCount = set_layouts.size(),
.pSetLayouts = set_layouts.data(),
.pushConstantRangeCount = push_constants.size(),
.pPushConstantRanges = push_constants.data()
};
CHECK_RESULT(vkCreatePipelineLayout(m_device, &create_info, nullptr, &m_pipeline_layout));
}
program& program::bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point)
{
VkDescriptorSet bind_sets[binding_set_index_max_enum];
unsigned count = 0;
for (auto& set : m_sets)
{
if (!set.m_device)
{
break;
}
bind_sets[count++] = set.m_descriptor_set.value(); // Current set pointer for binding
set.next_descriptor_set(); // Flush queue and update pointers
}
vkCmdBindPipeline(cmd, bind_point, m_pipeline);
vkCmdBindDescriptorSets(cmd, bind_point, m_pipeline_layout, 0, count, bind_sets, 0, nullptr);
return *this;
}
void descriptor_table_t::destroy()
{
if (!m_device)
{ {
return; return;
} }
next_descriptor_set(); vkDestroyDescriptorSetLayout(m_device, m_descriptor_set_layout, nullptr);
m_descriptor_set.push(image_descriptors, static_cast<u32>(count), type, binding_point); vk::get_resource_manager()->dispose(m_descriptor_pool);
for (int i = 0; i < count; ++i)
{
m_descriptors_dirty[binding_point] = false;
}
} }
VkDescriptorSet program::allocate_descriptor_set() void descriptor_table_t::init(VkDevice dev)
{
m_device = dev;
size_t bind_slots_count = 0;
for (auto& type_arr : m_inputs)
{
if (type_arr.empty() || type_arr.front().type == input_type_push_constant)
{
continue;
}
bind_slots_count += type_arr.size();
}
m_descriptor_slots.resize(bind_slots_count);
std::memset(m_descriptor_slots.data(), 0, sizeof(descriptor_slot_t) * bind_slots_count);
m_descriptors_dirty.resize(bind_slots_count);
std::fill(m_descriptors_dirty.begin(), m_descriptors_dirty.end(), false);
}
VkDescriptorSet descriptor_table_t::allocate_descriptor_set()
{ {
if (!m_descriptor_pool) if (!m_descriptor_pool)
{ {
create_descriptor_pool(); create_descriptor_pool();
create_descriptor_set_layout();
} }
return m_descriptor_pool->allocate(m_descriptor_set_layout); return m_descriptor_pool->allocate(m_descriptor_set_layout);
} }
void program::next_descriptor_set() void descriptor_table_t::next_descriptor_set()
{ {
const auto new_set = allocate_descriptor_set(); if (!m_descriptor_set)
const auto old_set = m_descriptor_set.value();
if (old_set)
{ {
m_copy_cmds.clear(); m_descriptor_set = allocate_descriptor_set();
for (unsigned i = 0; i < m_copy_cmds.size(); ++i) std::fill(m_descriptors_dirty.begin(), m_descriptors_dirty.end(), false);
{ return;
if (!m_descriptors_dirty[i])
{
continue;
}
// Reuse already initialized memory. Each command is the same anyway.
m_copy_cmds.resize(m_copy_cmds.size() + 1);
auto& cmd = m_copy_cmds.back();
cmd.srcBinding = cmd.dstBinding = i;
cmd.srcSet = old_set;
cmd.dstSet = new_set;
}
m_descriptor_set.push(m_copy_cmds);
} }
m_descriptor_set = allocate_descriptor_set(); // Check if we need to actually open a new set
if (!m_any_descriptors_dirty)
{
return;
}
auto old_set = m_descriptor_set.value();
auto new_set = allocate_descriptor_set();
auto push_descriptor_slot = [this](unsigned idx)
{
const auto& slot = m_descriptor_slots[idx];
const VkDescriptorType type = m_descriptor_types[idx];
if (auto ptr = std::get_if<VkDescriptorImageInfo>(&slot))
{
m_descriptor_set.push(*ptr, type, idx);
return;
}
if (auto ptr = std::get_if<VkDescriptorBufferInfo>(&slot))
{
m_descriptor_set.push(*ptr, type, idx);
return;
}
if (auto ptr = std::get_if<VkBufferView>(&slot))
{
m_descriptor_set.push(*ptr, type, idx);
return;
}
fmt::throw_exception("Unexpected descriptor structure at index %u", idx);
};
m_copy_cmds.clear();
for (unsigned i = 0; i < m_descriptor_slots.size(); ++i)
{
if (m_descriptors_dirty[i])
{
// Push
push_descriptor_slot(i);
m_descriptors_dirty[i] = false;
continue;
}
m_copy_cmds.push_back({
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
.srcSet = old_set,
.srcBinding = i,
.dstSet = new_set,
.dstBinding = i,
.descriptorCount = 1
});
}
m_descriptor_set.push(m_copy_cmds); // Write previous state
m_descriptor_set = new_set;
} }
program& program::bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point) void descriptor_table_t::create_descriptor_set_layout()
{
VkDescriptorSet set = m_descriptor_set.value();
vkCmdBindPipeline(cmd, bind_point, m_pipeline);
vkCmdBindDescriptorSets(cmd, bind_point, m_pipeline_layout, 0, 1, &set, 0, nullptr);
return *this;
}
void program::create_descriptor_set_layout()
{ {
ensure(m_descriptor_set_layout == VK_NULL_HANDLE); ensure(m_descriptor_set_layout == VK_NULL_HANDLE);
@ -425,7 +526,7 @@ namespace vk
m_descriptor_pool_sizes.clear(); m_descriptor_pool_sizes.clear();
m_descriptor_pool_sizes.reserve(input_type_max_enum); m_descriptor_pool_sizes.reserve(input_type_max_enum);
for (const auto& type_arr : uniforms) for (const auto& type_arr : m_inputs)
{ {
if (type_arr.empty() || type_arr.front().type == input_type_push_constant) if (type_arr.empty() || type_arr.front().type == input_type_push_constant)
{ {
@ -445,6 +546,13 @@ namespace vk
.stageFlags = to_shader_stage_flags(input.domain) .stageFlags = to_shader_stage_flags(input.domain)
}; };
bindings.push_back(binding); bindings.push_back(binding);
if (m_descriptor_types.size() < (input.location + 1))
{
m_descriptor_types.resize((input.location + 1));
}
m_descriptor_types[input.location] = type;
m_descriptor_pool_sizes.back().descriptorCount++; m_descriptor_pool_sizes.back().descriptorCount++;
} }
} }
@ -459,38 +567,31 @@ namespace vk
CHECK_RESULT(vkCreateDescriptorSetLayout(m_device, &set_layout_create_info, nullptr, &m_descriptor_set_layout)); CHECK_RESULT(vkCreateDescriptorSetLayout(m_device, &set_layout_create_info, nullptr, &m_descriptor_set_layout));
} }
void program::create_pipeline_layout() void descriptor_table_t::create_descriptor_pool()
{ {
ensure(!linked);
ensure(m_pipeline_layout == VK_NULL_HANDLE);
create_descriptor_set_layout();
rsx::simple_array<VkPushConstantRange> push_constants{};
for (const auto& input : uniforms[input_type_push_constant])
{
const auto& range = input.as_push_constant();
push_constants.push_back({ .offset = range.offset, .size = range.size });
}
VkPipelineLayoutCreateInfo create_info
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.flags = 0,
.setLayoutCount = 1,
.pSetLayouts = &m_descriptor_set_layout,
.pushConstantRangeCount = ::size32(push_constants),
.pPushConstantRanges = push_constants.data()
};
CHECK_RESULT(vkCreatePipelineLayout(m_device, &create_info, nullptr, &m_pipeline_layout));
}
void program::create_descriptor_pool()
{
ensure(linked);
m_descriptor_pool = std::make_unique<descriptor_pool>(); m_descriptor_pool = std::make_unique<descriptor_pool>();
m_descriptor_pool->create(*vk::get_current_renderer(), m_descriptor_pool_sizes); m_descriptor_pool->create(*vk::get_current_renderer(), m_descriptor_pool_sizes);
} }
void descriptor_table_t::validate() const
{
// Check for overlapping locations
std::set<u32> taken_locations;
for (auto& type_arr : m_inputs)
{
if (type_arr.empty() ||
type_arr.front().type == input_type_push_constant)
{
continue;
}
for (const auto& input : type_arr)
{
ensure(taken_locations.find(input.location) == taken_locations.end(), "Overlapping input locations found.");
taken_locations.insert(input.location);
}
}
}
} }
} }

View file

@ -54,7 +54,8 @@ namespace vk
using bound_data_t = std::variant<bound_buffer, bound_sampler, push_constant_ref>; using bound_data_t = std::variant<bound_buffer, bound_sampler, push_constant_ref>;
bound_data_t bound_data; bound_data_t bound_data;
u32 location; u32 set = 0;
u32 location = umax;
std::string name; std::string name;
inline bound_buffer& as_buffer() { return *std::get_if<bound_buffer>(&bound_data); } inline bound_buffer& as_buffer() { return *std::get_if<bound_buffer>(&bound_data); }
@ -69,6 +70,7 @@ namespace vk
::glsl::program_domain domain, ::glsl::program_domain domain,
const std::string& name, const std::string& name,
program_input_type type, program_input_type type,
u32 set,
u32 location, u32 location,
const bound_data_t& data = bound_buffer{}) const bound_data_t& data = bound_buffer{})
{ {
@ -77,38 +79,13 @@ namespace vk
.domain = domain, .domain = domain,
.type = type, .type = type,
.bound_data = data, .bound_data = data,
.set = set,
.location = location, .location = location,
.name = name .name = name
}; };
} }
}; };
union descriptor_slot_t
{
VkDescriptorImageInfo image_info;
VkDescriptorBufferInfo buffer_info;
VkBufferView buffer_view;
bool matches(const VkDescriptorImageInfo& test) const
{
return test.imageView == image_info.imageView &&
test.sampler == image_info.sampler &&
test.imageLayout == image_info.imageLayout;
}
bool matches(const VkDescriptorBufferInfo& test) const
{
return test.buffer == buffer_info.buffer &&
test.offset == buffer_info.offset &&
test.range == buffer_info.range;
}
bool matches(VkBufferView test) const
{
return test == buffer_view;
}
};
class shader class shader
{ {
::glsl::program_domain type = ::glsl::program_domain::glsl_vertex_program; ::glsl::program_domain type = ::glsl::program_domain::glsl_vertex_program;
@ -132,37 +109,71 @@ namespace vk
VkShaderModule get_handle() const; VkShaderModule get_handle() const;
}; };
class program using descriptor_slot_t = std::variant<VkDescriptorImageInfo, VkDescriptorBufferInfo, VkBufferView>;
struct descriptor_table_t
{ {
std::array<std::vector<program_input>, input_type_max_enum> uniforms;
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
std::array<std::vector<program_input>, input_type_max_enum> m_inputs;
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
std::array<u32, 16> fs_texture_bindings;
std::array<u32, 16> fs_texture_mirror_bindings;
std::array<u32, 4> vs_texture_bindings;
bool linked = false;
std::unique_ptr<vk::descriptor_pool> m_descriptor_pool; std::unique_ptr<vk::descriptor_pool> m_descriptor_pool;
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE; VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;
vk::descriptor_set m_descriptor_set{}; vk::descriptor_set m_descriptor_set{};
rsx::simple_array<VkDescriptorPoolSize> m_descriptor_pool_sizes; rsx::simple_array<VkDescriptorPoolSize> m_descriptor_pool_sizes;
rsx::simple_array<VkDescriptorType> m_descriptor_types;
std::vector<descriptor_slot_t> m_descriptor_slots; std::vector<descriptor_slot_t> m_descriptor_slots;
std::vector<bool> m_descriptors_dirty; std::vector<bool> m_descriptors_dirty;
rsx::simple_array<VkCopyDescriptorSet> m_copy_cmds; rsx::simple_array<VkCopyDescriptorSet> m_copy_cmds;
bool m_any_descriptors_dirty = false;
void init(); void init(VkDevice dev);
void destroy();
void validate() const;
void create_descriptor_set_layout(); void create_descriptor_set_layout();
void create_pipeline_layout();
void create_descriptor_pool(); void create_descriptor_pool();
VkDescriptorSet allocate_descriptor_set(); VkDescriptorSet allocate_descriptor_set();
void next_descriptor_set(); void next_descriptor_set();
template <typename T>
inline void notify_descriptor_slot_updated(u32 slot, const T& data)
{
m_descriptors_dirty[slot] = true;
m_descriptor_slots[slot] = data;
m_any_descriptors_dirty = true;
}
};
enum binding_set_index : u32
{
// For separate shader objects
binding_set_index_vertex = 0,
binding_set_index_fragment = 1,
// Aliases
binding_set_index_compute = 0,
binding_set_index_unified = 0,
// Meta
binding_set_index_max_enum = 2,
};
class program
{
VkDevice m_device = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
std::variant<VkGraphicsPipelineCreateInfo, VkComputePipelineCreateInfo> m_info;
std::array<descriptor_table_t, binding_set_index_max_enum> m_sets;
bool m_linked = false;
void init();
void create_pipeline_layout();
program& load_uniforms(const std::vector<program_input>& inputs); program& load_uniforms(const std::vector<program_input>& inputs);
public: public:
@ -173,20 +184,18 @@ namespace vk
program(program&& other) = delete; program(program&& other) = delete;
~program(); ~program();
program& link(); program& link(bool separate_stages);
program& bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point); program& bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point);
bool has_uniform(program_input_type type, const std::string &uniform_name); bool has_uniform(program_input_type type, const std::string &uniform_name);
u32 get_uniform_location(program_input_type type, const std::string& uniform_name); std::pair<u32, u32> get_uniform_location(::glsl::program_domain domain, program_input_type type, const std::string& uniform_name);
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorType type); void bind_uniform(const VkDescriptorImageInfo &image_descriptor, u32 set_id, u32 binding_point);
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, bool is_stencil_mirror = false); void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 set_id, u32 binding_point);
void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point); void bind_uniform(const VkBufferView &buffer_view, u32 set_id, u32 binding_point);
void bind_uniform(const VkBufferView &buffer_view, u32 binding_point); void bind_uniform(const VkBufferView &buffer_view, ::glsl::program_domain domain, program_input_type type, const std::string &binding_name);
void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name);
void bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point, VkDescriptorType type);
void bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, VkDescriptorType type, int count, u32 binding_point); void bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, VkDescriptorType type, int count, u32 set_id, u32 binding_point);
inline VkPipelineLayout layout() const { return m_pipeline_layout; } inline VkPipelineLayout layout() const { return m_pipeline_layout; }
inline VkPipeline value() const { return m_pipeline; } inline VkPipeline value() const { return m_pipeline; }

View file

@ -31,6 +31,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program, ::glsl::program_domain::glsl_compute_program,
"multisampled", "multisampled",
glsl::input_type_storage_texture, glsl::input_type_storage_texture,
0,
0 0
), ),
@ -38,6 +39,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program, ::glsl::program_domain::glsl_compute_program,
"resolve", "resolve",
glsl::input_type_storage_texture, glsl::input_type_storage_texture,
0,
1 1
), ),
}; };
@ -51,8 +53,8 @@ namespace vk
{ {
auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_VIEW_MULTISAMPLED)); auto msaa_view = multisampled->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_VIEW_MULTISAMPLED));
auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY)); auto resolved_view = resolve->get_view(rsx::default_remap_vector.with_encoding(VK_REMAP_IDENTITY));
m_program->bind_uniform({ VK_NULL_HANDLE, msaa_view->value, multisampled->current_layout }, "multisampled", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); m_program->bind_uniform({ VK_NULL_HANDLE, msaa_view->value, multisampled->current_layout }, 0, 0);
m_program->bind_uniform({ VK_NULL_HANDLE, resolved_view->value, resolve->current_layout }, "resolve", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); m_program->bind_uniform({ VK_NULL_HANDLE, resolved_view->value, resolve->current_layout }, 0, 1);
} }
void run(const vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image) void run(const vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image)
@ -116,6 +118,7 @@ namespace vk
::glsl::glsl_fragment_program, ::glsl::glsl_fragment_program,
"push_constants", "push_constants",
glsl::input_type_push_constant, glsl::input_type_push_constant,
0,
umax, umax,
glsl::push_constant_ref{ .size = 16 } glsl::push_constant_ref{ .size = 16 }
)); ));

View file

@ -471,7 +471,7 @@ namespace vk
void shader_interpreter::update_fragment_textures(const std::array<VkDescriptorImageInfo, 68>& sampled_images) void shader_interpreter::update_fragment_textures(const std::array<VkDescriptorImageInfo, 68>& sampled_images)
{ {
// FIXME: Cannot use m_fragment_textures.start now since each interpreter has its own binding layout // FIXME: Cannot use m_fragment_textures.start now since each interpreter has its own binding layout
u32 binding = m_current_interpreter->get_uniform_location(glsl::input_type_texture, "texture1D_array"); auto [set, binding] = m_current_interpreter->get_uniform_location(::glsl::glsl_fragment_program, glsl::input_type_texture, "texture1D_array");
if (binding == umax) if (binding == umax)
{ {
return; return;
@ -480,7 +480,7 @@ namespace vk
const VkDescriptorImageInfo* texture_ptr = sampled_images.data(); const VkDescriptorImageInfo* texture_ptr = sampled_images.data();
for (u32 i = 0; i < 4; ++i, ++binding, texture_ptr += 16) for (u32 i = 0; i < 4; ++i, ++binding, texture_ptr += 16)
{ {
m_current_interpreter->bind_uniform_array(texture_ptr, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 16, binding); m_current_interpreter->bind_uniform_array(texture_ptr, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 16, set, binding);
} }
} }

View file

@ -6,7 +6,6 @@
#include "vkutils/device.h" #include "vkutils/device.h"
#include "../Program/GLSLCommon.h" #include "../Program/GLSLCommon.h"
std::string VKVertexDecompilerThread::getFloatTypeName(usz elementCount) std::string VKVertexDecompilerThread::getFloatTypeName(usz elementCount)
{ {
return glsl::getFloatTypeNameImpl(elementCount); return glsl::getFloatTypeNameImpl(elementCount);
@ -27,14 +26,57 @@ std::string VKVertexDecompilerThread::compareFunction(COMPARE f, const std::stri
return glsl::compareFunctionImpl(f, Op0, Op1, scalar); return glsl::compareFunctionImpl(f, Op0, Op1, scalar);
} }
void VKVertexDecompilerThread::prepareBindingTable()
{
u32 location = 0;
vk_prog->binding_table.vertex_buffers_location = location;
location += 3; // Persistent verts, volatile and layout data
vk_prog->binding_table.context_buffer_location = location++;
if (m_device_props.emulate_conditional_rendering)
{
vk_prog->binding_table.cr_pred_buffer_location = location++;
}
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
const bool is_texture_type = PT.type.starts_with("sampler");
for (const ParamItem& PI : PT.items)
{
if (is_texture_type)
{
const int id = vk::get_texture_index(PI.name);
vk_prog->binding_table.vtex_location[id] = location++;
continue;
}
if (PI.name.starts_with("vc["))
{
if (!(m_prog.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS))
{
vk_prog->binding_table.cbuf_location = location++;
continue;
}
vk_prog->binding_table.instanced_lut_buffer_location = location++;
vk_prog->binding_table.instanced_cbuf_location = location++;
continue;
}
}
}
}
void VKVertexDecompilerThread::insertHeader(std::stringstream &OS) void VKVertexDecompilerThread::insertHeader(std::stringstream &OS)
{ {
prepareBindingTable();
OS << OS <<
"#version 450\n\n" "#version 450\n\n"
"#extension GL_ARB_separate_shader_objects : enable\n\n"; "#extension GL_ARB_separate_shader_objects : enable\n\n";
OS << OS <<
"layout(std140, set = 0, binding = 0) uniform VertexContextBuffer\n" "layout(std140, set = 0, binding = " << vk_prog->binding_table.context_buffer_location << " ) uniform VertexContextBuffer\n"
"{\n" "{\n"
" mat4 scale_offset_mat;\n" " mat4 scale_offset_mat;\n"
" ivec4 user_clip_enabled[2];\n" " ivec4 user_clip_enabled[2];\n"
@ -45,13 +87,31 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS)
" float z_far;\n" " float z_far;\n"
"};\n\n"; "};\n\n";
vk::glsl::program_input context_input =
{
.domain = glsl::glsl_vertex_program,
.type = vk::glsl::input_type_uniform_buffer,
.location = vk_prog->binding_table.context_buffer_location,
.name = "VertexContextBuffer"
};
inputs.push_back(context_input);
if (m_device_props.emulate_conditional_rendering) if (m_device_props.emulate_conditional_rendering)
{ {
OS << OS <<
"layout(std430, set = 0, binding = 8) readonly buffer EXT_Conditional_Rendering\n" "layout(std430, set = 0, binding = " << vk_prog->binding_table.cr_pred_buffer_location << ") readonly buffer EXT_Conditional_Rendering\n"
"{\n" "{\n"
" uint conditional_rendering_predicate;\n" " uint conditional_rendering_predicate;\n"
"};\n\n"; "};\n\n";
vk::glsl::program_input predicate_input =
{
.domain = glsl::glsl_vertex_program,
.type = vk::glsl::input_type_storage_buffer,
.location = vk_prog->binding_table.cr_pred_buffer_location,
.name = "EXT_Conditional_Rendering"
};
inputs.push_back(predicate_input);
} }
OS << OS <<
@ -63,52 +123,50 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS)
" uint layout_ptr_offset;\n" " uint layout_ptr_offset;\n"
" uint xform_constants_offset;\n"; " uint xform_constants_offset;\n";
u32 push_constants_size = 5 * sizeof(u32);
if (m_device_props.emulate_conditional_rendering) if (m_device_props.emulate_conditional_rendering)
{ {
push_constants_size += sizeof(u32);
OS << " uint conditional_rendering_enabled;\n"; OS << " uint conditional_rendering_enabled;\n";
} }
OS << "};\n\n"; OS << "};\n\n";
vk::glsl::program_input in; vk::glsl::program_input push_constants =
in.location = m_binding_table.vertex_params_bind_slot; {
in.domain = glsl::glsl_vertex_program; .domain = glsl::glsl_vertex_program,
in.name = "VertexContextBuffer"; .type = vk::glsl::input_type_push_constant,
in.type = vk::glsl::input_type_uniform_buffer; .bound_data = vk::glsl::push_constant_ref{ .offset = 0, .size = push_constants_size }
inputs.push_back(in); };
inputs.push_back(push_constants);
} }
void VKVertexDecompilerThread::insertInputs(std::stringstream& OS, const std::vector<ParamType>& /*inputs*/) void VKVertexDecompilerThread::insertInputs(std::stringstream& OS, const std::vector<ParamType>& /*inputs*/)
{ {
OS << "layout(set=0, binding=5) uniform usamplerBuffer persistent_input_stream;\n"; // Data stream with persistent vertex data (cacheable) static const char* input_streams[] =
OS << "layout(set=0, binding=6) uniform usamplerBuffer volatile_input_stream;\n"; // Data stream with per-draw data (registers and immediate draw data) {
OS << "layout(set=0, binding=7) uniform usamplerBuffer vertex_layout_stream;\n"; // Data stream defining vertex data layout "persistent_input_stream", // Data stream with persistent vertex data (cacheable)
"volatile_input_stream", // Data stream with per-draw data (registers and immediate draw data)
"vertex_layout_stream" // Data stream defining vertex data layout"
};
vk::glsl::program_input in; int location = vk_prog->binding_table.vertex_buffers_location;
in.location = m_binding_table.vertex_buffers_first_bind_slot; for (const auto& stream : input_streams)
in.domain = glsl::glsl_vertex_program; {
in.name = "persistent_input_stream"; OS << "layout(set=0, binding=" << location << ") uniform usamplerBuffer " << stream << ";\n";
in.type = vk::glsl::input_type_texel_buffer;
this->inputs.push_back(in);
in.location = m_binding_table.vertex_buffers_first_bind_slot + 1; vk::glsl::program_input in;
in.domain = glsl::glsl_vertex_program; in.location = location++;
in.name = "volatile_input_stream"; in.domain = glsl::glsl_vertex_program;
in.type = vk::glsl::input_type_texel_buffer; in.name = stream;
this->inputs.push_back(in); in.type = vk::glsl::input_type_texel_buffer;
this->inputs.push_back(in);
in.location = m_binding_table.vertex_buffers_first_bind_slot + 2; }
in.domain = glsl::glsl_vertex_program;
in.name = "vertex_layout_stream";
in.type = vk::glsl::input_type_texel_buffer;
this->inputs.push_back(in);
} }
void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std::vector<ParamType> & constants) void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std::vector<ParamType> & constants)
{ {
vk::glsl::program_input in; vk::glsl::program_input in;
u32 location = m_binding_table.vertex_textures_first_bind_slot;
for (const ParamType &PT : constants) for (const ParamType &PT : constants)
{ {
for (const ParamItem &PI : PT.items) for (const ParamItem &PI : PT.items)
@ -117,12 +175,12 @@ void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std
{ {
if (!(m_prog.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS)) if (!(m_prog.ctrl & RSX_SHADER_CONTROL_INSTANCED_CONSTANTS))
{ {
OS << "layout(std430, set=0, binding=" << static_cast<int>(m_binding_table.vertex_constant_buffers_bind_slot) << ") readonly buffer VertexConstantsBuffer\n"; OS << "layout(std430, set=0, binding=" << vk_prog->binding_table.cbuf_location << ") readonly buffer VertexConstantsBuffer\n";
OS << "{\n"; OS << "{\n";
OS << " vec4 vc[];\n"; OS << " vec4 vc[];\n";
OS << "};\n\n"; OS << "};\n\n";
in.location = m_binding_table.vertex_constant_buffers_bind_slot; in.location = vk_prog->binding_table.cbuf_location;
in.domain = glsl::glsl_vertex_program; in.domain = glsl::glsl_vertex_program;
in.name = "VertexConstantsBuffer"; in.name = "VertexConstantsBuffer";
in.type = vk::glsl::input_type_storage_buffer; in.type = vk::glsl::input_type_storage_buffer;
@ -133,26 +191,26 @@ void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std
else else
{ {
// 1. Bind indirection lookup buffer // 1. Bind indirection lookup buffer
OS << "layout(std430, set=0, binding=" << static_cast<int>(m_binding_table.instancing_lookup_table_bind_slot) << ") readonly buffer InstancingData\n"; OS << "layout(std430, set=0, binding=" << vk_prog->binding_table.instanced_lut_buffer_location << ") readonly buffer InstancingData\n";
OS << "{\n"; OS << "{\n";
OS << " int constants_addressing_lookup[];\n"; OS << " int constants_addressing_lookup[];\n";
OS << "};\n\n"; OS << "};\n\n";
in.location = m_binding_table.instancing_lookup_table_bind_slot; in.location = vk_prog->binding_table.instanced_lut_buffer_location;
in.domain = glsl::glsl_vertex_program; in.domain = glsl::glsl_vertex_program;
in.name = "InstancingData"; in.name = "InstancingData";
in.type = vk::glsl::input_type_storage_buffer; in.type = vk::glsl::input_type_storage_buffer;
inputs.push_back(in); inputs.push_back(in);
// 2. Bind actual constants buffer // 2. Bind actual constants buffer
OS << "layout(std430, set=0, binding=" << static_cast<int>(m_binding_table.instancing_constants_buffer_slot) << ") readonly buffer VertexConstantsBuffer\n"; OS << "layout(std430, set=0, binding=" << vk_prog->binding_table.instanced_cbuf_location << ") readonly buffer VertexConstantsBuffer\n";
OS << "{\n"; OS << "{\n";
OS << " vec4 instanced_constants_array[];\n"; OS << " vec4 instanced_constants_array[];\n";
OS << "};\n\n"; OS << "};\n\n";
OS << "#define CONSTANTS_ARRAY_LENGTH " << (properties.has_indexed_constants ? 468 : ::size32(m_constant_ids)) << "\n\n"; OS << "#define CONSTANTS_ARRAY_LENGTH " << (properties.has_indexed_constants ? 468 : ::size32(m_constant_ids)) << "\n\n";
in.location = m_binding_table.instancing_constants_buffer_slot; in.location = vk_prog->binding_table.instanced_cbuf_location;
in.domain = glsl::glsl_vertex_program; in.domain = glsl::glsl_vertex_program;
in.name = "VertexConstantsBuffer"; in.name = "VertexConstantsBuffer";
in.type = vk::glsl::input_type_storage_buffer; in.type = vk::glsl::input_type_storage_buffer;
@ -166,7 +224,8 @@ void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std
PT.type == "sampler1D" || PT.type == "sampler1D" ||
PT.type == "sampler3D") PT.type == "sampler3D")
{ {
in.location = location; const int id = vk::get_texture_index(PI.name);
in.location = vk_prog->binding_table.vtex_location[id];
in.name = PI.name; in.name = PI.name;
in.type = vk::glsl::input_type_texture; in.type = vk::glsl::input_type_texture;
@ -190,7 +249,7 @@ void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std
} }
} }
OS << "layout(set = 0, binding=" << location++ << ") uniform " << samplerType << " " << PI.name << ";\n"; OS << "layout(set = 0, binding=" << in.location << ") uniform " << samplerType << " " << PI.name << ";\n";
} }
} }
} }
@ -371,8 +430,6 @@ void VKVertexDecompilerThread::insertMainEnd(std::stringstream & OS)
void VKVertexDecompilerThread::Task() void VKVertexDecompilerThread::Task()
{ {
m_device_props.emulate_conditional_rendering = vk::emulate_conditional_rendering(); m_device_props.emulate_conditional_rendering = vk::emulate_conditional_rendering();
m_binding_table = vk::g_render_device->get_pipeline_binding_table();
m_shader = Decompile(); m_shader = Decompile();
vk_prog->SetInputs(inputs); vk_prog->SetInputs(inputs);
} }

View file

@ -15,7 +15,6 @@ struct VKVertexDecompilerThread : public VertexProgramDecompiler
std::string &m_shader; std::string &m_shader;
std::vector<vk::glsl::program_input> inputs; std::vector<vk::glsl::program_input> inputs;
class VKVertexProgram *vk_prog; class VKVertexProgram *vk_prog;
vk::pipeline_binding_table m_binding_table{};
struct struct
{ {
@ -36,6 +35,8 @@ protected:
void insertMainStart(std::stringstream &OS) override; void insertMainStart(std::stringstream &OS) override;
void insertMainEnd(std::stringstream &OS) override; void insertMainEnd(std::stringstream &OS) override;
void prepareBindingTable();
const RSXVertexProgram &rsx_vertex_program; const RSXVertexProgram &rsx_vertex_program;
public: public:
VKVertexDecompilerThread(const RSXVertexProgram &prog, std::string& shader, ParamArray&, class VKVertexProgram &dst) VKVertexDecompilerThread(const RSXVertexProgram &prog, std::string& shader, ParamArray&, class VKVertexProgram &dst)
@ -61,6 +62,19 @@ public:
vk::glsl::shader shader; vk::glsl::shader shader;
std::vector<vk::glsl::program_input> uniforms; std::vector<vk::glsl::program_input> uniforms;
// Quick attribute indices
struct
{
u32 context_buffer_location = umax; // Vertex program context
u32 cr_pred_buffer_location = umax; // Conditional rendering predicate
u32 vertex_buffers_location = umax; // Vertex input streams (3)
u32 cbuf_location = umax; // Vertex program constants register file
u32 instanced_lut_buffer_location = umax; // Instancing redirection table
u32 instanced_cbuf_location = umax; // Instancing constants register file
u32 vtex_location[4]; // Vertex textures (inf)
} binding_table;
void Decompile(const RSXVertexProgram& prog); void Decompile(const RSXVertexProgram& prog);
void Compile(); void Compile();
void SetInputs(std::vector<vk::glsl::program_input>& inputs); void SetInputs(std::vector<vk::glsl::program_input>& inputs);

View file

@ -76,6 +76,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program, ::glsl::program_domain::glsl_compute_program,
"InputTexture", "InputTexture",
vk::glsl::input_type_texture, vk::glsl::input_type_texture,
0,
0 0
), ),
@ -83,6 +84,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program, ::glsl::program_domain::glsl_compute_program,
"OutputTexture", "OutputTexture",
vk::glsl::input_type_storage_texture, vk::glsl::input_type_storage_texture,
0,
1 1
), ),
}; };
@ -103,8 +105,8 @@ namespace vk
VK_FALSE, 0.f, 1.f, 0.f, 0.f, VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK); VK_FALSE, 0.f, 1.f, 0.f, 0.f, VK_FILTER_LINEAR, VK_FILTER_LINEAR, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
} }
m_program->bind_uniform({ m_sampler->value, m_input_image->value, m_input_image->image()->current_layout }, "InputTexture", VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); m_program->bind_uniform({ m_sampler->value, m_input_image->value, m_input_image->image()->current_layout }, 0, 0);
m_program->bind_uniform({ VK_NULL_HANDLE, m_output_image->value, m_output_image->image()->current_layout }, "OutputTexture", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); m_program->bind_uniform({ VK_NULL_HANDLE, m_output_image->value, m_output_image->image()->current_layout }, 0, 1);
} }
void fsr_pass::run(const vk::command_buffer& cmd, vk::viewable_image* src, vk::viewable_image* dst, const size2u& input_size, const size2u& output_size) void fsr_pass::run(const vk::command_buffer& cmd, vk::viewable_image* src, vk::viewable_image* dst, const size2u& input_size, const size2u& output_size)

View file

@ -333,11 +333,6 @@ namespace vk
return &m_handle; return &m_handle;
} }
VkDescriptorSet descriptor_set::value() const
{
return m_handle;
}
void descriptor_set::push(const VkBufferView& buffer_view, VkDescriptorType type, u32 binding) void descriptor_set::push(const VkBufferView& buffer_view, VkDescriptorType type, u32 binding)
{ {
m_push_type_mask |= (1ull << type); m_push_type_mask |= (1ull << type);

View file

@ -94,8 +94,10 @@ namespace vk
void swap(descriptor_set& other); void swap(descriptor_set& other);
descriptor_set& operator = (VkDescriptorSet set); descriptor_set& operator = (VkDescriptorSet set);
VkDescriptorSet value() const { return m_handle; }
operator bool() const { return m_handle != VK_NULL_HANDLE; }
VkDescriptorSet* ptr(); VkDescriptorSet* ptr();
VkDescriptorSet value() const;
void push(const VkBufferView& buffer_view, VkDescriptorType type, u32 binding); void push(const VkBufferView& buffer_view, VkDescriptorType type, u32 binding);
void push(const VkDescriptorBufferInfo& buffer_info, VkDescriptorType type, u32 binding); void push(const VkDescriptorBufferInfo& buffer_info, VkDescriptorType type, u32 binding);
void push(const VkDescriptorImageInfo& image_info, VkDescriptorType type, u32 binding); void push(const VkDescriptorImageInfo& image_info, VkDescriptorType type, u32 binding);