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);
}
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;
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,
"ssbo" + std::to_string(i),
glsl::program_input_type::input_type_storage_buffer,
0,
i
);
result.push_back(input);
@ -31,6 +32,7 @@ namespace vk
"push_constants",
glsl::program_input_type::input_type_push_constant,
0,
0,
glsl::push_constant_ref{ .offset = 0, .size = push_constants_size }
);
result.push_back(input);
@ -243,7 +245,7 @@ namespace vk
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)
@ -289,7 +291,7 @@ namespace vk
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)
@ -349,8 +351,8 @@ namespace vk
void cs_aggregator::bind_resources()
{
m_program->bind_buffer({ src->value, 0, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
m_program->bind_buffer({ dst->value, 0, 4 }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
m_program->bind_uniform({ src->value, 0, block_length }, 0, 0);
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)

View file

@ -344,7 +344,7 @@ namespace vk
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)
@ -445,8 +445,8 @@ namespace vk
void bind_resources() override
{
m_program->bind_buffer({ src_buffer->value, in_offset, block_length }, 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
m_program->bind_buffer({ dst_buffer->value, out_offset, block_length }, 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
m_program->bind_uniform({ src_buffer->value, in_offset, block_length }, 0, 0);
m_program->bind_uniform({ dst_buffer->value, out_offset, block_length }, 0, 1);
}
void set_parameters(const vk::command_buffer& cmd)
@ -573,9 +573,9 @@ namespace vk
void bind_resources() override
{
const auto op = static_cast<int>(Op);
m_program->bind_buffer({ src_buffer->value, in_offset, in_block_length }, 0 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
m_program->bind_buffer({ dst_buffer->value, out_offset, out_block_length }, 1 ^ op, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
const auto op = static_cast<u32>(Op);
m_program->bind_uniform({ src_buffer->value, in_offset, in_block_length }, 0u, 0u ^ op);
m_program->bind_uniform({ dst_buffer->value, out_offset, out_block_length }, 0u, 1u ^ op);
}
void set_parameters(const vk::command_buffer& cmd)

View file

@ -554,8 +554,8 @@ bool VKGSRender::bind_texture_env()
if (view) [[likely]]
{
m_program->bind_uniform({ fs_sampler_handles[i]->value, view->value, view->image()->current_layout },
i,
::glsl::program_domain::glsl_fragment_program);
vk::glsl::binding_set_index_fragment,
m_fragment_prog->binding_table.ftex_location[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 },
i,
::glsl::program_domain::glsl_fragment_program,
true);
vk::glsl::binding_set_index_fragment,
m_fragment_prog->binding_table.ftex_stencil_location[i]);
}
}
else
{
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 },
i,
::glsl::program_domain::glsl_fragment_program);
vk::glsl::binding_set_index_fragment,
m_fragment_prog->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 },
i,
::glsl::program_domain::glsl_fragment_program,
true);
vk::glsl::binding_set_index_fragment,
m_fragment_prog->binding_table.ftex_stencil_location[i]);
}
}
}
@ -606,8 +604,8 @@ 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 },
i,
::glsl::program_domain::glsl_vertex_program);
vk::glsl::binding_set_index_vertex,
m_vertex_prog->binding_table.vtex_location[i]);
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));
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,
::glsl::program_domain::glsl_vertex_program);
vk::glsl::binding_set_index_vertex,
m_vertex_prog->binding_table.vtex_location[i]);
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);
m_program->bind_uniform({ vs_sampler_handles[i]->value, image_ptr->value, image_ptr->image()->current_layout },
i,
::glsl::program_domain::glsl_vertex_program);
vk::glsl::binding_set_index_vertex,
m_vertex_prog->binding_table.vtex_location[i]);
}
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;
bool update_descriptors = false;
const auto& binding_table = m_device->get_pipeline_binding_table();
if (m_current_draw.subdraw_id == 0)
{
update_descriptors = true;
@ -878,9 +874,10 @@ void VKGSRender::emit_geometry(u32 sub_index)
ensure(m_vertex_layout_storage);
if (update_descriptors)
{
m_program->bind_uniform(persistent_buffer, binding_table.vertex_buffers_first_bind_slot);
m_program->bind_uniform(volatile_buffer, binding_table.vertex_buffers_first_bind_slot + 1);
m_program->bind_uniform(m_vertex_layout_storage->value, binding_table.vertex_buffers_first_bind_slot + 2);
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);
}
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);
}
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)
{
prepareBindingTable();
std::vector<const char*> required_extensions;
if (device_props.has_native_half_support)
@ -97,21 +174,18 @@ void VKFragmentDecompilerThread::insertOutputs(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])
{
if (PT.type != "sampler1D" &&
PT.type != "sampler2D" &&
PT.type != "sampler3D" &&
PT.type != "samplerCube")
if (PT.type.starts_with("sampler1D"))
{
continue;
}
for (const ParamItem& PI : PT.items)
{
std::string samplerType = PT.type;
ensure(PI.name.length() > 3);
int index = atoi(&PI.name[3]);
const int index = vk::get_texture_index(PI.name);
const auto mask = (1 << index);
if (properties.multisampled_sampler_mask & mask)
@ -135,39 +209,37 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
}
}
vk::glsl::program_input in;
in.location = location;
in.domain = glsl::glsl_fragment_program;
in.name = PI.name;
in.type = vk::glsl::input_type_texture;
const int id = vk::get_texture_index(PI.name);
auto in = vk::glsl::program_input::make(
glsl::glsl_fragment_program,
PI.name,
vk::glsl::input_type_texture,
vk::glsl::binding_set_index_fragment,
vk_prog->binding_table.ftex_location[id]
);
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)
{
// Insert stencil mirror declaration
in.name += "_stencil";
in.location = location;
in.location = vk_prog->binding_table.ftex_stencil_location[id];
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;
for (const ParamType& PT : m_parr.params[PF_PARAM_UNIFORM])
{
if (PT.type == "sampler1D" ||
PT.type == "sampler2D" ||
PT.type == "sampler3D" ||
PT.type == "samplerCube")
if (PT.type.starts_with("sampler1D"))
{
continue;
}
for (const ParamItem& PI : PT.items)
{
@ -177,13 +249,13 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
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 << constants_block;
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 << " float fog_param0;\n";
OS << " float fog_param1;\n";
@ -195,32 +267,39 @@ void VKFragmentDecompilerThread::insertConstants(std::stringstream & OS)
OS << " float wpos_bias;\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 << " sampler_info texture_parameters[16];\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 << " uvec4 stipple_pattern[8];\n";
OS << "};\n\n";
vk::glsl::program_input in;
in.location = m_binding_table.fragment_constant_buffers_bind_slot;
in.domain = glsl::glsl_fragment_program;
in.name = "FragmentConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
inputs.push_back(in);
vk::glsl::program_input in
{
.domain = glsl::glsl_fragment_program,
.type = vk::glsl::input_type_uniform_buffer,
.set = vk::glsl::binding_set_index_fragment
};
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";
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";
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";
inputs.push_back(in);
}
@ -372,7 +451,6 @@ void VKFragmentDecompilerThread::insertMainEnd(std::stringstream & OS)
void VKFragmentDecompilerThread::Task()
{
m_binding_table = vk::g_render_device->get_pipeline_binding_table();
m_shader = Decompile();
vk_prog->SetInputs(inputs);
}

View file

@ -19,7 +19,8 @@ struct VKFragmentDecompilerThread : public FragmentProgramDecompiler
std::vector<vk::glsl::program_input> inputs;
class VKFragmentProgram *vk_prog;
glsl::shader_properties m_shader_props{};
vk::pipeline_binding_table m_binding_table{};
void prepareBindingTable();
public:
VKFragmentDecompilerThread(std::string& shader, ParamArray& parr, const RSXFragmentProgram &prog, u32& size, class VKFragmentProgram& dst)
@ -32,6 +33,7 @@ public:
void Task();
const std::vector<vk::glsl::program_input>& get_inputs() { return inputs; }
protected:
std::string getFloatTypeName(usz elementCount) override;
std::string getHalfTypeName(usz elementCount) override;
@ -63,8 +65,19 @@ public:
std::vector<usz> FragmentConstantOffsetCache;
std::array<u32, 4> output_color_masks{ {} };
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);
/**
* 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_buffer(m_vertex_constants_buffer_info, binding_table.vertex_constant_buffers_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
m_program->bind_uniform(m_fragment_env_buffer_info, binding_table.fragment_state_bind_slot);
m_program->bind_uniform(m_fragment_texture_params_buffer_info, binding_table.fragment_texture_params_bind_slot);
m_program->bind_uniform(m_raster_env_buffer_info, binding_table.rasterizer_env_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_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, 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 (!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_buffer(m_fragment_instructions_buffer_info, m_shader_interpreter.get_fragment_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);
}
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_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)
{
m_program->bind_buffer(m_instancing_indirection_buffer_info, binding_table.instancing_lookup_table_bind_slot, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
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_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);
}
// Clear flags

View file

@ -63,21 +63,21 @@ namespace vk
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 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);
}
for (u32 n = 0; n < m_num_usable_samplers; ++n, ++binding)
{
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);
}
for (u32 n = 0; n < m_num_input_attachments; ++n, ++binding)
{
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);
}
@ -179,13 +179,14 @@ namespace vk
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)
{
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);
@ -488,6 +489,7 @@ namespace vk
"push_constants",
glsl::input_type_push_constant,
0,
0,
glsl::push_constant_ref { .size = 68 }
)
);
@ -503,6 +505,7 @@ namespace vk
"push_constants",
glsl::input_type_push_constant,
0,
0,
glsl::push_constant_ref {.offset = 68, .size = 12 }
)
);
@ -715,6 +718,7 @@ namespace vk
"push_constants",
vk::glsl::input_type_push_constant,
0,
0,
glsl::push_constant_ref{ .size = 32 })
};
}
@ -864,6 +868,7 @@ namespace vk
"push_constants",
vk::glsl::input_type_push_constant,
0,
0,
glsl::push_constant_ref{ .size = 16 }
)
);

View file

@ -54,7 +54,9 @@ namespace vk
const VkComputePipelineCreateInfo& create_info,
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(
@ -62,7 +64,9 @@ namespace vk
const std::vector<glsl::program_input>& vs_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(
@ -171,7 +175,7 @@ namespace vk
op_flags flags, callback_t callback,
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);
}
@ -187,7 +191,7 @@ namespace vk
const std::vector<glsl::program_input>& fs_inputs)
{
// 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);
}
@ -200,7 +204,7 @@ namespace vk
const std::vector<glsl::program_input>& fs_inputs)
{
VkShaderModule modules[] = { vs, fs };
if (flags == COMPILE_INLINE)
if (flags & COMPILE_INLINE)
{
return int_compile_graphics_pipe(create_info, modules, vs_inputs, fs_inputs);
}

View file

@ -53,13 +53,16 @@ namespace vk
class pipe_compiler
{
public:
enum op_flags
enum op_flag_bits
{
COMPILE_DEFAULT = 0,
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>&)>;
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;
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)
{
switch (type)
@ -120,42 +144,24 @@ namespace vk
void program::init()
{
linked = false;
fs_texture_bindings.fill(~0u);
fs_texture_mirror_bindings.fill(~0u);
vs_texture_bindings.fill(~0u);
m_linked = false;
}
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();
load_uniforms(vertex_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)
: m_device(dev)
: m_device(dev), m_info(create_info)
{
init();
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()
@ -165,257 +171,352 @@ namespace vk
if (m_pipeline_layout)
{
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)
{
ensure(!linked); // "Cannot change uniforms in already linked program!"
ensure(!m_linked); // "Cannot change uniforms in already linked program!"
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;
}
program& program::link()
program& program::link(bool separate_objects)
{
// Preprocess texture bindings
// Link step is only useful for rasterizer programs, compute programs do not need this
for (const auto &uniform : uniforms[program_input_type::input_type_texture])
{
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);
auto p_graphics_info = std::get_if<VkGraphicsPipelineCreateInfo>(&m_info);
auto p_compute_info = !p_graphics_info ? std::get_if<VkComputePipelineCreateInfo>(&m_info) : nullptr;
const bool is_graphics_pipe = p_graphics_info != nullptr;
if (name_start == 0)
if (!is_graphics_pipe) [[ likely ]]
{
// Fragment texture (tex...)
if (name_end == umax)
// We only support compute and graphics, so disable this for compute
separate_objects = false;
}
if (!separate_objects)
{
// Normal texture
fs_texture_bindings[index] = uniform.location;
// 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)
{
if (type_arr.empty())
{
continue;
}
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();
}
}
sink.validate();
sink.init(m_device);
}
else
{
// Stencil mirror
fs_texture_mirror_bindings[index] = uniform.location;
}
}
else
for (auto& set : m_sets)
{
// Vertex texture (vtex...)
vs_texture_bindings[index] = uniform.location;
for (auto& type_arr : set.m_inputs)
{
if (type_arr.empty())
{
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;
}
bool program::has_uniform(program_input_type type, const std::string& uniform_name)
{
const auto& uniform = uniforms[type];
for (auto& set : m_sets)
{
const auto& uniform = set.m_inputs[type];
return std::any_of(uniform.cbegin(), uniform.cend(), [&uniform_name](const auto& u)
{
return u.name == uniform_name;
});
}
}
u32 program::get_uniform_location(program_input_type type, const std::string& uniform_name)
std::pair<u32, u32> program::get_uniform_location(::glsl::program_domain domain, 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)
for (unsigned i = 0; i < ::size32(m_sets); ++i)
{
return u.name == uniform_name;
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 == uniform.end())
if (result != type_arr.end())
{
return { umax };
return { i, result->location };
}
}
return result->location;
return { umax, umax };
}
void program::bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string& uniform_name, VkDescriptorType type)
void program::bind_uniform(const VkDescriptorImageInfo& image_descriptor, u32 set_id, u32 binding_point)
{
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))
if (m_sets[set_id].m_descriptor_slots[binding_point] == image_descriptor)
{
return;
}
next_descriptor_set();
m_descriptor_set.push(image_descriptor, type, uniform.location);
m_descriptors_dirty[uniform.location] = false;
return;
}
m_sets[set_id].notify_descriptor_slot_updated(binding_point, image_descriptor);
}
rsx_log.notice("texture not found in program: %s", uniform_name.c_str());
}
void program::bind_uniform(const VkDescriptorImageInfo & image_descriptor, int texture_unit, ::glsl::program_domain domain, bool is_stencil_mirror)
void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 set_id, u32 binding_point)
{
ensure(domain != ::glsl::program_domain::glsl_compute_program);
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))
if (m_sets[set_id].m_descriptor_slots[binding_point] == buffer_descriptor)
{
return;
}
next_descriptor_set();
m_descriptor_set.push(image_descriptor, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, binding);
m_descriptors_dirty[binding] = false;
m_sets[set_id].notify_descriptor_slot_updated(binding_point, buffer_descriptor);
}
void program::bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point)
void program::bind_uniform(const VkBufferView &buffer_view, u32 set_id, u32 binding_point)
{
bind_buffer(buffer_descriptor, binding_point, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
}
void program::bind_uniform(const VkBufferView &buffer_view, u32 binding_point)
{
if (m_descriptor_slots[binding_point].matches(buffer_view))
if (m_sets[set_id].m_descriptor_slots[binding_point] == buffer_view)
{
return;
}
next_descriptor_set();
m_descriptor_set.push(buffer_view, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, binding_point);
m_descriptors_dirty[binding_point] = false;
m_sets[set_id].notify_descriptor_slot_updated(binding_point, buffer_view);
}
void program::bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name)
void program::bind_uniform_array(const VkDescriptorImageInfo* image_descriptors, VkDescriptorType type, int count, u32 set_id, u32 binding_point)
{
for (const auto &uniform : uniforms[type])
{
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;
}
next_descriptor_set();
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)
{
// FIXME: Unoptimized...
bool match = true;
auto& set = m_sets[set_id];
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;
}
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;
}
next_descriptor_set();
m_descriptor_set.push(image_descriptors, static_cast<u32>(count), type, binding_point);
vkDestroyDescriptorSetLayout(m_device, m_descriptor_set_layout, nullptr);
vk::get_resource_manager()->dispose(m_descriptor_pool);
}
for (int i = 0; i < count; ++i)
void descriptor_table_t::init(VkDevice dev)
{
m_descriptors_dirty[binding_point] = false;
}
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;
}
VkDescriptorSet program::allocate_descriptor_set()
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)
{
create_descriptor_pool();
create_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();
const auto old_set = m_descriptor_set.value();
if (!m_descriptor_set)
{
m_descriptor_set = allocate_descriptor_set();
std::fill(m_descriptors_dirty.begin(), m_descriptors_dirty.end(), false);
return;
}
if (old_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_copy_cmds.size(); ++i)
for (unsigned i = 0; i < m_descriptor_slots.size(); ++i)
{
if (!m_descriptors_dirty[i])
if (m_descriptors_dirty[i])
{
// Push
push_descriptor_slot(i);
m_descriptors_dirty[i] = false;
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_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);
m_descriptor_set.push(m_copy_cmds); // Write previous state
m_descriptor_set = new_set;
}
m_descriptor_set = allocate_descriptor_set();
}
program& program::bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point)
{
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()
void descriptor_table_t::create_descriptor_set_layout()
{
ensure(m_descriptor_set_layout == VK_NULL_HANDLE);
@ -425,7 +526,7 @@ namespace vk
m_descriptor_pool_sizes.clear();
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)
{
@ -445,6 +546,13 @@ namespace vk
.stageFlags = to_shader_stage_flags(input.domain)
};
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++;
}
}
@ -459,38 +567,31 @@ namespace vk
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->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>;
bound_data_t bound_data;
u32 location;
u32 set = 0;
u32 location = umax;
std::string name;
inline bound_buffer& as_buffer() { return *std::get_if<bound_buffer>(&bound_data); }
@ -69,6 +70,7 @@ namespace vk
::glsl::program_domain domain,
const std::string& name,
program_input_type type,
u32 set,
u32 location,
const bound_data_t& data = bound_buffer{})
{
@ -77,38 +79,13 @@ namespace vk
.domain = domain,
.type = type,
.bound_data = data,
.set = set,
.location = location,
.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
{
::glsl::program_domain type = ::glsl::program_domain::glsl_vertex_program;
@ -132,37 +109,71 @@ namespace vk
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;
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::array<std::vector<program_input>, input_type_max_enum> m_inputs;
std::unique_ptr<vk::descriptor_pool> m_descriptor_pool;
VkDescriptorSetLayout m_descriptor_set_layout = VK_NULL_HANDLE;
vk::descriptor_set m_descriptor_set{};
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<bool> m_descriptors_dirty;
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_pipeline_layout();
void create_descriptor_pool();
VkDescriptorSet allocate_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);
public:
@ -173,20 +184,18 @@ namespace vk
program(program&& other) = delete;
~program();
program& link();
program& link(bool separate_stages);
program& bind(const vk::command_buffer& cmd, VkPipelineBindPoint bind_point);
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, int texture_unit, ::glsl::program_domain domain, bool is_stencil_mirror = false);
void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 binding_point);
void bind_uniform(const VkBufferView &buffer_view, u32 binding_point);
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(const VkDescriptorImageInfo &image_descriptor, u32 set_id, u32 binding_point);
void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, u32 set_id, u32 binding_point);
void bind_uniform(const VkBufferView &buffer_view, u32 set_id, 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_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 VkPipeline value() const { return m_pipeline; }

View file

@ -31,6 +31,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program,
"multisampled",
glsl::input_type_storage_texture,
0,
0
),
@ -38,6 +39,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program,
"resolve",
glsl::input_type_storage_texture,
0,
1
),
};
@ -51,8 +53,8 @@ namespace vk
{
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));
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, resolved_view->value, resolve->current_layout }, "resolve", 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 }, 0, 1);
}
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,
"push_constants",
glsl::input_type_push_constant,
0,
umax,
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)
{
// 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)
{
return;
@ -480,7 +480,7 @@ namespace vk
const VkDescriptorImageInfo* texture_ptr = sampled_images.data();
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 "../Program/GLSLCommon.h"
std::string VKVertexDecompilerThread::getFloatTypeName(usz 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);
}
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)
{
prepareBindingTable();
OS <<
"#version 450\n\n"
"#extension GL_ARB_separate_shader_objects : enable\n\n";
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"
" mat4 scale_offset_mat;\n"
" ivec4 user_clip_enabled[2];\n"
@ -45,13 +87,31 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS)
" float z_far;\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)
{
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"
" uint conditional_rendering_predicate;\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 <<
@ -63,52 +123,50 @@ void VKVertexDecompilerThread::insertHeader(std::stringstream &OS)
" uint layout_ptr_offset;\n"
" uint xform_constants_offset;\n";
u32 push_constants_size = 5 * sizeof(u32);
if (m_device_props.emulate_conditional_rendering)
{
push_constants_size += sizeof(u32);
OS << " uint conditional_rendering_enabled;\n";
}
OS << "};\n\n";
vk::glsl::program_input in;
in.location = m_binding_table.vertex_params_bind_slot;
in.domain = glsl::glsl_vertex_program;
in.name = "VertexContextBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
inputs.push_back(in);
vk::glsl::program_input push_constants =
{
.domain = glsl::glsl_vertex_program,
.type = vk::glsl::input_type_push_constant,
.bound_data = vk::glsl::push_constant_ref{ .offset = 0, .size = push_constants_size }
};
inputs.push_back(push_constants);
}
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)
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
static const char* input_streams[] =
{
"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"
};
int location = vk_prog->binding_table.vertex_buffers_location;
for (const auto& stream : input_streams)
{
OS << "layout(set=0, binding=" << location << ") uniform usamplerBuffer " << stream << ";\n";
vk::glsl::program_input in;
in.location = m_binding_table.vertex_buffers_first_bind_slot;
in.location = location++;
in.domain = glsl::glsl_vertex_program;
in.name = "persistent_input_stream";
in.type = vk::glsl::input_type_texel_buffer;
this->inputs.push_back(in);
in.location = m_binding_table.vertex_buffers_first_bind_slot + 1;
in.domain = glsl::glsl_vertex_program;
in.name = "volatile_input_stream";
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.name = 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)
{
vk::glsl::program_input in;
u32 location = m_binding_table.vertex_textures_first_bind_slot;
for (const ParamType &PT : constants)
{
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))
{
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 << " vec4 vc[];\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.name = "VertexConstantsBuffer";
in.type = vk::glsl::input_type_storage_buffer;
@ -133,26 +191,26 @@ void VKVertexDecompilerThread::insertConstants(std::stringstream & OS, const std
else
{
// 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 << " int constants_addressing_lookup[];\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.name = "InstancingData";
in.type = vk::glsl::input_type_storage_buffer;
inputs.push_back(in);
// 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 << " vec4 instanced_constants_array[];\n";
OS << "};\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.name = "VertexConstantsBuffer";
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 == "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.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()
{
m_device_props.emulate_conditional_rendering = vk::emulate_conditional_rendering();
m_binding_table = vk::g_render_device->get_pipeline_binding_table();
m_shader = Decompile();
vk_prog->SetInputs(inputs);
}

View file

@ -15,7 +15,6 @@ struct VKVertexDecompilerThread : public VertexProgramDecompiler
std::string &m_shader;
std::vector<vk::glsl::program_input> inputs;
class VKVertexProgram *vk_prog;
vk::pipeline_binding_table m_binding_table{};
struct
{
@ -36,6 +35,8 @@ protected:
void insertMainStart(std::stringstream &OS) override;
void insertMainEnd(std::stringstream &OS) override;
void prepareBindingTable();
const RSXVertexProgram &rsx_vertex_program;
public:
VKVertexDecompilerThread(const RSXVertexProgram &prog, std::string& shader, ParamArray&, class VKVertexProgram &dst)
@ -61,6 +62,19 @@ public:
vk::glsl::shader shader;
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 Compile();
void SetInputs(std::vector<vk::glsl::program_input>& inputs);

View file

@ -76,6 +76,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program,
"InputTexture",
vk::glsl::input_type_texture,
0,
0
),
@ -83,6 +84,7 @@ namespace vk
::glsl::program_domain::glsl_compute_program,
"OutputTexture",
vk::glsl::input_type_storage_texture,
0,
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);
}
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({ VK_NULL_HANDLE, m_output_image->value, m_output_image->image()->current_layout }, "OutputTexture", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
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 }, 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)

View file

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

View file

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