vk: Integrate shader interpreter

This commit is contained in:
kd-11 2020-04-09 20:50:27 +03:00 committed by Ivan
parent 0072df7f20
commit b4bf48c33b
18 changed files with 891 additions and 87 deletions

View file

@ -4,6 +4,8 @@ layout(location=1) out vec4 ocol1;
layout(location=2) out vec4 ocol2;
layout(location=3) out vec4 ocol3;
layout(location=0) in vec4 in_regs[16];
#define RSX_FP_OPCODE_NOP 0x00 // No-Operation
#define RSX_FP_OPCODE_MOV 0x01 // Move
#define RSX_FP_OPCODE_MUL 0x02 // Multiply
@ -174,15 +176,17 @@ vec4 read_src(const in int index)
// TODO: wpos
value = vec4(0.); break;
case 1:
value = gl_FrontFacing? in_regs[0] : in_regs[2]; break;
case 2:
value = gl_FrontFacing? in_regs[1] : in_regs[3]; break;
case 2:
value = gl_FrontFacing? in_regs[2] : in_regs[4]; break;
case 3:
value = fetch_fog_value(fog_mode, in_regs[4]); break;
value = fetch_fog_value(fog_mode, in_regs[5]); break;
case 13:
value = in_regs[6]; break;
case 14:
value = gl_FrontFacing? vec4(1.) : vec4(-1.); break;
default:
value = in_regs[i + 1]; break;
value = in_regs[i + 3]; break;
}
break;

View file

@ -1,5 +1,8 @@
R"(
// Program outputs
layout(location=0) out vec4 dest[16];
#define RSX_SCA_OPCODE_NOP 0x00 // No-Operation
#define RSX_SCA_OPCODE_MOV 0x01 // Move (copy)
#define RSX_SCA_OPCODE_RCP 0x02 // Reciprocal
@ -193,7 +196,6 @@ uvec4 instr;
vec4 temp[32];
ivec4 a[2] = { ivec4(0), ivec4(0) };
vec4 cc[2] = { vec4(0), vec4(0) };
vec4 dest[16];
D0 d0;
D1 d1;
@ -248,15 +250,11 @@ void write_vec(in vec4 value)
}
}
vec4 write_output(const in int oid, const in int mask_bit)
void write_output(const in int oid, const in int mask_bit)
{
if (attribute_enabled(1 << mask_bit))
if (!attribute_enabled(1 << mask_bit))
{
return dest[oid];
}
else
{
return vec4(0., 0., 0., 1.);
dest[oid] = vec4(0., 0., 0., 1.);
}
}
@ -527,21 +525,19 @@ void main()
}
// TODO: 2-sided lighting
if (attribute_enabled(1 << 0 | 1 << 2))
if (!attribute_enabled(1 << 0 | 1 << 2))
{
diff_color = dest[1];
diff_color1 = dest[1];
dest[1] = dest[3] = vec4(0, 0, 0, 1);
}
if (attribute_enabled(1 << 1 | 1 << 3))
if (!attribute_enabled(1 << 1 | 1 << 3))
{
spec_color = dest[2];
spec_color1 = dest[2];
dest[2] = dest[4] = vec4(0, 0, 0, 1);
}
if (attribute_enabled(1 << 4))
if (!attribute_enabled(1 << 4))
{
fog_c = dest[5].xxxx;
dest[5].x = 0;
}
if (attribute_enabled(1 << 5))
@ -567,19 +563,23 @@ void main()
gl_ClipDistance[5] = (user_clip_enabled[1].y > 0)? dest[6].w * user_clip_factor[1].y : 0.5f;
}
tc8 = write_output(15, 12);
tc9 = write_output(6, 13);
tc0 = write_output(7, 14);
tc1 = write_output(8, 15);
tc2 = write_output(9, 16);
tc3 = write_output(10, 17);
tc4 = write_output(11, 18);
tc5 = write_output(12, 19);
tc6 = write_output(13, 20);
tc7 = write_output(14, 21);
write_output(15, 12);
write_output(6, 13);
write_output(7, 14);
write_output(8, 15);
write_output(9, 16);
write_output(10, 17);
write_output(11, 18);
write_output(12, 19);
write_output(13, 20);
write_output(14, 21);
vec4 pos = dest[0] * scale_offset_mat;
#ifdef Z_NEGATIVE_ONE_TO_ONE
pos.z = (pos.z + pos.z) - pos.w;
#endif
gl_Position = pos;
}

View file

@ -5,7 +5,7 @@ namespace program_common
{
namespace interpreter
{
std::string get_vertex_interpreter()
static std::string get_vertex_interpreter()
{
const char* s =
#include "Interpreter/VertexInterpreter.glsl"
@ -13,7 +13,7 @@ namespace program_common
return s;
}
std::string get_fragment_interpreter()
static std::string get_fragment_interpreter()
{
const char* s =
#include "Interpreter/FragmentInterpreter.glsl"

View file

@ -101,9 +101,11 @@ namespace gl
std::stringstream builder;
comp.insertHeader(builder);
builder << "#define Z_NEGATIVE_ONE_TO_ONE\n\n";
comp.insertConstants(builder, {});
comp.insertInputs(builder, {});
comp.insertOutputs(builder, {});
// Insert vp stream input
builder << "\n"
@ -180,10 +182,6 @@ namespace gl
::glsl::insert_subheader_block(builder);
comp.insertConstants(builder);
// Declare custom inputs
builder <<
"layout(location=1) in vec4 in_regs[15];\n\n";
const char* type_names[] = { "sampler1D", "sampler2D", "samplerCube", "sampler3D" };
for (int i = 0; i < 4; ++i)
{

View file

@ -60,7 +60,7 @@ namespace vk
}
// Reserve descriptor pools
m_descriptor_pool.create(*get_current_renderer(), descriptor_pool_sizes.data(), ::size32(descriptor_pool_sizes), VK_MAX_COMPUTE_TASKS, 2);
m_descriptor_pool.create(*get_current_renderer(), descriptor_pool_sizes.data(), ::size32(descriptor_pool_sizes), VK_MAX_COMPUTE_TASKS, 3);
VkDescriptorSetLayoutCreateInfo infos = {};
infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
@ -179,7 +179,7 @@ namespace vk
VkPipeline pipeline;
vkCreateComputePipelines(*get_current_renderer(), nullptr, 1, &info, nullptr, &pipeline);
m_program = std::make_unique<vk::glsl::program>(*get_current_renderer(), pipeline);
m_program = std::make_unique<vk::glsl::program>(*get_current_renderer(), pipeline, m_pipeline_layout);
declare_inputs();
}

View file

@ -571,6 +571,115 @@ void VKGSRender::bind_texture_env()
}
}
void VKGSRender::bind_interpreter_texture_env()
{
std::array<VkDescriptorImageInfo, 68> texture_env;
VkDescriptorImageInfo fallback = { vk::null_sampler(), vk::null_image_view(*m_current_command_buffer, VK_IMAGE_VIEW_TYPE_1D)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL };
auto start = texture_env.begin();
auto end = start;
// Fill default values
// 1D
std::advance(end, 16);
std::fill(start, end, fallback);
// 2D
start = end;
fallback.imageView = vk::null_image_view(*m_current_command_buffer, VK_IMAGE_VIEW_TYPE_2D)->value;
std::advance(end, 16);
std::fill(start, end, fallback);
// 3D
start = end;
fallback.imageView = vk::null_image_view(*m_current_command_buffer, VK_IMAGE_VIEW_TYPE_3D)->value;
std::advance(end, 16);
std::fill(start, end, fallback);
// CUBE
start = end;
fallback.imageView = vk::null_image_view(*m_current_command_buffer, VK_IMAGE_VIEW_TYPE_CUBE)->value;
std::advance(end, 16);
std::fill(start, end, fallback);
for (int i = 0; i < rsx::limits::fragment_textures_count; ++i)
{
if (current_fp_metadata.referenced_textures_mask & (1 << i))
{
vk::image_view* view = nullptr;
auto sampler_state = static_cast<vk::texture_cache::sampled_image_descriptor*>(fs_sampler_state[i].get());
if (rsx::method_registers.fragment_textures[i].enabled() &&
sampler_state->validate())
{
if (view = sampler_state->image_handle; !view)
{
//Requires update, copy subresource
view = m_texture_cache.create_temporary_subresource(*m_current_command_buffer, sampler_state->external_subresource_desc);
}
else
{
switch (auto raw = view->image(); raw->current_layout)
{
default:
//case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
verify(HERE), sampler_state->upload_context == rsx::texture_upload_context::blit_engine_dst;
raw->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
verify(HERE), sampler_state->upload_context == rsx::texture_upload_context::blit_engine_src;
raw->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
break;
case VK_IMAGE_LAYOUT_GENERAL:
verify(HERE), sampler_state->upload_context == rsx::texture_upload_context::framebuffer_storage;
if (!sampler_state->is_cyclic_reference)
{
// This was used in a cyclic ref before, but is missing a barrier
// No need for a full stall, use a custom barrier instead
VkPipelineStageFlags src_stage;
VkAccessFlags src_access;
if (raw->aspect() == VK_IMAGE_ASPECT_COLOR_BIT)
{
src_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
}
else
{
src_stage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
}
vk::insert_image_memory_barrier(
*m_current_command_buffer,
raw->value,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
src_stage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
src_access, VK_ACCESS_SHADER_READ_BIT,
{ raw->aspect(), 0, 1, 0, 1 });
raw->current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
verify(HERE), sampler_state->upload_context == rsx::texture_upload_context::framebuffer_storage, !sampler_state->is_cyclic_reference;
raw->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
break;
}
}
}
if (view)
{
const int offsets[] = { 0, 16, 48, 32 };
auto& sampled_image_info = texture_env[offsets[static_cast<u32>(sampler_state->image_type)] + i];
sampled_image_info = { fs_sampler_handles[i]->value, view->value, view->image()->current_layout };
}
}
}
m_shader_interpreter.update_fragment_textures(texture_env, m_current_frame->descriptor_set);
}
void VKGSRender::emit_geometry(u32 sub_index)
{
auto &draw_call = rsx::method_registers.current_draw_clause;
@ -701,7 +810,7 @@ void VKGSRender::emit_geometry(u32 sub_index)
}
// Bind the new set of descriptors for use with this draw call
vkCmdBindDescriptorSets(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &m_current_frame->descriptor_set, 0, nullptr);
vkCmdBindDescriptorSets(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline_layout, 0, 1, &m_current_frame->descriptor_set, 0, nullptr);
m_frame_stats.setup_time += m_profiler.duration();
@ -860,7 +969,15 @@ void VKGSRender::end()
load_program_env();
m_frame_stats.setup_time += m_profiler.duration();
bind_texture_env();
if (!m_shader_interpreter.is_interpreter(m_program)) [[likely]]
{
bind_texture_env();
}
else
{
bind_interpreter_texture_env();
}
m_texture_cache.release_uncached_temporary_subresources();
m_frame_stats.textures_upload_time += m_profiler.duration();

View file

@ -5,8 +5,15 @@
#include "VulkanAPI.h"
#include "VKHelpers.h"
namespace vk
{
class shader_interpreter;
}
struct VKFragmentDecompilerThread : public FragmentProgramDecompiler
{
friend class vk::shader_interpreter;
std::string& m_shader;
ParamArray& m_parrDummy;
std::vector<vk::glsl::program_input> inputs;

View file

@ -465,6 +465,12 @@ VKGSRender::VKGSRender() : GSRender()
m_index_buffer_ring_info.create(VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_INDEX_RING_BUFFER_SIZE_M * 0x100000, "index buffer");
m_texture_upload_buffer_ring_info.create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_TEXTURE_UPLOAD_RING_BUFFER_SIZE_M * 0x100000, "texture upload buffer", 32 * 0x100000);
if (g_cfg.video.shader_interpreter_mode != shader_interpreter_mode::disabled)
{
m_vertex_instructions_buffer.create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, 16 * 0x100000, "vertex instructions buffer", 512 * 16);
m_fragment_instructions_buffer.create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, 16 * 0x100000, "fragment instructions buffer", 2048);
}
const auto limits = m_device->gpu().get_limits();
m_texbuffer_view_size = std::min(limits.maxTexelBufferElements, VK_ATTRIB_RING_BUFFER_SIZE_M * 0x100000u);
@ -544,6 +550,11 @@ VKGSRender::VKGSRender() : GSRender()
m_occlusion_query_pool.initialize(*m_current_command_buffer);
if (g_cfg.video.shader_interpreter_mode != shader_interpreter_mode::disabled)
{
m_shader_interpreter.init(*m_device);
}
backend_config.supports_multidraw = true;
// NOTE: We do not actually need multiple sample support for A2C to work
@ -582,6 +593,7 @@ VKGSRender::~VKGSRender()
//Shaders
vk::finalize_compiler_context();
m_prog_buffer->clear();
m_shader_interpreter.destroy();
m_persistent_attribute_storage.reset();
m_volatile_attribute_storage.reset();
@ -600,6 +612,8 @@ VKGSRender::~VKGSRender()
m_transform_constants_ring_info.destroy();
m_index_buffer_ring_info.destroy();
m_texture_upload_buffer_ring_info.destroy();
m_vertex_instructions_buffer.destroy();
m_fragment_instructions_buffer.destroy();
//Fallback bindables
null_buffer.reset();
@ -937,19 +951,26 @@ void VKGSRender::check_descriptors()
VkDescriptorSet VKGSRender::allocate_descriptor_set()
{
verify(HERE), m_current_frame->used_descriptors < DESCRIPTOR_MAX_DRAW_CALLS;
if (!m_shader_interpreter.is_interpreter(m_program)) [[likely]]
{
verify(HERE), m_current_frame->used_descriptors < DESCRIPTOR_MAX_DRAW_CALLS;
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.descriptorPool = m_current_frame->descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &descriptor_layouts;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.descriptorPool = m_current_frame->descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &descriptor_layouts;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
VkDescriptorSet new_descriptor_set;
CHECK_RESULT(vkAllocateDescriptorSets(*m_device, &alloc_info, &new_descriptor_set));
m_current_frame->used_descriptors++;
VkDescriptorSet new_descriptor_set;
CHECK_RESULT(vkAllocateDescriptorSets(*m_device, &alloc_info, &new_descriptor_set));
m_current_frame->used_descriptors++;
return new_descriptor_set;
return new_descriptor_set;
}
else
{
return m_shader_interpreter.allocate_descriptor_set();
}
}
void VKGSRender::set_viewport()
@ -1408,7 +1429,7 @@ void VKGSRender::do_local_task(rsx::FIFO_state state)
bool VKGSRender::load_program()
{
if (m_graphics_state & rsx::pipeline_state::invalidate_pipeline_bits)
if (m_interpreter_state = (m_graphics_state & rsx::pipeline_state::invalidate_pipeline_bits))
{
get_current_fragment_program(fs_sampler_state);
verify(HERE), current_fragment_program.valid;
@ -1552,38 +1573,82 @@ bool VKGSRender::load_program()
}
properties.renderpass_key = m_current_renderpass_key;
vk::glsl::program* active_interpreter = nullptr;
vk::enter_uninterruptible();
if (!m_interpreter_state && m_pipeline_properties == properties) [[likely]]
{
// Nothing changed
if (m_shader_interpreter.is_interpreter(m_program))
{
if (g_cfg.video.shader_interpreter_mode == shader_interpreter_mode::forced)
{
return true;
}
//Load current program from buffer
vertex_program.skip_vertex_input_check = true;
fragment_program.unnormalized_coords = 0;
m_program = m_prog_buffer->get_graphics_pipeline(vertex_program, fragment_program, properties,
active_interpreter = m_program;
}
else
{
return true;
}
}
if (g_cfg.video.shader_interpreter_mode != shader_interpreter_mode::forced) [[likely]]
{
vk::enter_uninterruptible();
//Load current program from buffer
vertex_program.skip_vertex_input_check = true;
fragment_program.unnormalized_coords = 0;
m_program = m_prog_buffer->get_graphics_pipeline(vertex_program, fragment_program, properties,
!g_cfg.video.disable_asynchronous_shader_compiler, true, *m_device, pipeline_layout).get();
vk::leave_uninterruptible();
vk::leave_uninterruptible();
if (m_prog_buffer->check_cache_missed())
{
// Notify the user with HUD notification
if (g_cfg.misc.show_shader_compilation_hint)
if (m_prog_buffer->check_cache_missed())
{
if (m_overlay_manager)
// Notify the user with HUD notification
if (g_cfg.misc.show_shader_compilation_hint)
{
if (auto dlg = m_overlay_manager->get<rsx::overlays::shader_compile_notification>())
if (m_overlay_manager)
{
// Extend duration
dlg->touch();
}
else
{
// Create dialog but do not show immediately
m_overlay_manager->create<rsx::overlays::shader_compile_notification>();
if (auto dlg = m_overlay_manager->get<rsx::overlays::shader_compile_notification>())
{
// Extend duration
dlg->touch();
}
else
{
// Create dialog but do not show immediately
m_overlay_manager->create<rsx::overlays::shader_compile_notification>();
}
}
}
}
}
else
{
m_program = nullptr;
}
if (!m_program && g_cfg.video.shader_interpreter_mode != shader_interpreter_mode::disabled)
{
if (!m_shader_interpreter.is_interpreter(old_program))
{
m_interpreter_state = rsx::invalidate_pipeline_bits;
}
if (active_interpreter) [[likely]]
{
m_program = active_interpreter;
}
else
{
m_program = m_shader_interpreter.get(properties);
}
}
m_pipeline_properties = properties;
return m_program != nullptr;
}
@ -1601,6 +1666,7 @@ void VKGSRender::load_program_env()
const bool update_vertex_env = !!(m_graphics_state & rsx::pipeline_state::vertex_state_dirty);
const bool update_fragment_env = !!(m_graphics_state & rsx::pipeline_state::fragment_state_dirty);
const bool update_fragment_texture_env = !!(m_graphics_state & rsx::pipeline_state::fragment_texture_state_dirty);
const bool update_instruction_buffers = (!!m_interpreter_state && m_shader_interpreter.is_interpreter(m_program));
if (update_vertex_env)
{
@ -1634,7 +1700,7 @@ void VKGSRender::load_program_env()
m_vertex_constants_buffer_info = { m_transform_constants_ring_info.heap->value, mem, 8192 };
}
if (update_fragment_constants)
if (update_fragment_constants && !update_instruction_buffers)
{
check_heap_status(VK_HEAP_CHECK_FRAGMENT_CONSTANTS_STORAGE);
@ -1680,14 +1746,63 @@ void VKGSRender::load_program_env()
m_fragment_texture_params_buffer_info = { m_fragment_texture_params_ring_info.heap->value, mem, 256 };
}
if (update_instruction_buffers)
{
if (m_interpreter_state & rsx::vertex_program_dirty)
{
// Attach vertex buffer data
const auto vp_block_length = current_vp_metadata.ucode_length + 16;
auto vp_mapping = m_vertex_instructions_buffer.alloc<256>(vp_block_length);
auto vp_buf = static_cast<u8*>(m_vertex_instructions_buffer.map(vp_mapping, vp_block_length));
auto vp_config = reinterpret_cast<u32*>(vp_buf);
vp_config[0] = current_vertex_program.base_address;
vp_config[1] = current_vertex_program.entry;
vp_config[2] = current_vertex_program.output_mask;
std::memcpy(vp_buf + 16, current_vertex_program.data.data(), current_vp_metadata.ucode_length);
m_vertex_instructions_buffer.unmap();
m_vertex_instructions_buffer_info = { m_vertex_instructions_buffer.heap->value, vp_mapping, vp_block_length };
}
if (m_interpreter_state & rsx::fragment_program_dirty)
{
// Attach fragment buffer data
const auto fp_block_length = current_fp_metadata.program_ucode_length + 16;
auto fp_mapping = m_fragment_instructions_buffer.alloc<256>(fp_block_length);
auto fp_buf = static_cast<u8*>(m_fragment_instructions_buffer.map(fp_mapping, fp_block_length));
// Control mask
const auto control_masks = reinterpret_cast<u32*>(fp_buf);
control_masks[0] = rsx::method_registers.shader_control();
control_masks[1] = current_fragment_program.texture_dimensions;
const auto fp_data = static_cast<u8*>(current_fragment_program.addr) + current_fp_metadata.program_start_offset;
std::memcpy(fp_buf + 16, fp_data, current_fp_metadata.program_ucode_length);
m_fragment_instructions_buffer.unmap();
m_fragment_instructions_buffer_info = { m_fragment_instructions_buffer.heap->value, fp_mapping, fp_block_length };
}
}
const auto& binding_table = m_device->get_pipeline_binding_table();
m_program->bind_uniform(m_vertex_env_buffer_info, binding_table.vertex_params_bind_slot, m_current_frame->descriptor_set);
m_program->bind_uniform(m_vertex_constants_buffer_info, binding_table.vertex_constant_buffers_bind_slot, m_current_frame->descriptor_set);
m_program->bind_uniform(m_fragment_constants_buffer_info, binding_table.fragment_constant_buffers_bind_slot, m_current_frame->descriptor_set);
m_program->bind_uniform(m_fragment_env_buffer_info, binding_table.fragment_state_bind_slot, m_current_frame->descriptor_set);
m_program->bind_uniform(m_fragment_texture_params_buffer_info, binding_table.fragment_texture_params_bind_slot, m_current_frame->descriptor_set);
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_current_frame->descriptor_set);
}
else
{
m_program->bind_buffer(m_vertex_instructions_buffer_info, m_shader_interpreter.get_vertex_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
m_program->bind_buffer(m_fragment_instructions_buffer_info, m_shader_interpreter.get_fragment_instruction_location(), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, m_current_frame->descriptor_set);
}
if (vk::emulate_conditional_rendering())
{
auto predicate = m_cond_render_buffer ? m_cond_render_buffer->value : vk::get_scratch_buffer()->value;

View file

@ -8,6 +8,7 @@
#include "VKOverlays.h"
#include "VKProgramBuffer.h"
#include "VKFramebuffer.h"
#include "VKShaderInterpreter.h"
#include "../GCM.h"
#include <thread>
@ -349,7 +350,8 @@ private:
private:
VKFragmentProgram m_fragment_prog;
VKVertexProgram m_vertex_prog;
vk::glsl::program *m_program;
vk::glsl::program *m_program = nullptr;
vk::pipeline_props m_pipeline_properties;
vk::texture_cache m_texture_cache;
rsx::vk_render_targets m_rtts;
@ -428,6 +430,9 @@ private:
vk::data_heap m_index_buffer_ring_info; // Index data
vk::data_heap m_texture_upload_buffer_ring_info; // Texture upload heap
vk::data_heap m_fragment_instructions_buffer;
vk::data_heap m_vertex_instructions_buffer;
VkDescriptorBufferInfo m_vertex_env_buffer_info;
VkDescriptorBufferInfo m_fragment_env_buffer_info;
VkDescriptorBufferInfo m_vertex_layout_stream_info;
@ -435,6 +440,9 @@ private:
VkDescriptorBufferInfo m_fragment_constants_buffer_info;
VkDescriptorBufferInfo m_fragment_texture_params_buffer_info;
VkDescriptorBufferInfo m_vertex_instructions_buffer_info;
VkDescriptorBufferInfo m_fragment_instructions_buffer_info;
std::array<vk::frame_context_t, VK_MAX_ASYNC_FRAMES> frame_context_storage;
//Temp frame context to use if the real frame queue is overburdened. Only used for storage
vk::frame_context_t m_aux_frame_context;
@ -464,6 +472,9 @@ private:
//Vertex layout
rsx::vertex_input_layout m_vertex_layout;
vk::shader_interpreter m_shader_interpreter;
u32 m_interpreter_state;
#if defined(HAVE_X11) && defined(HAVE_VULKAN)
Display *m_display_handle = nullptr;
#endif
@ -512,6 +523,7 @@ private:
void load_texture_env();
void bind_texture_env();
void bind_interpreter_texture_env();
public:
void init_buffers(rsx::framebuffer_creation_context context, bool skip_reading = false);

View file

@ -3629,11 +3629,12 @@ public:
public:
VkPipeline pipeline;
VkPipelineLayout pipeline_layout;
u64 attribute_location_mask;
u64 vertex_attributes_mask;
program(VkDevice dev, VkPipeline p, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs);
program(VkDevice dev, VkPipeline p);
program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs);
program(VkDevice dev, VkPipeline p, VkPipelineLayout layout);
program(const program&) = delete;
program(program&& other) = delete;
~program();
@ -3649,6 +3650,7 @@ public:
void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, VkDescriptorSet &descriptor_set);
void bind_buffer(const VkDescriptorBufferInfo &buffer_descriptor, uint32_t binding_point, VkDescriptorType type, VkDescriptorSet &descriptor_set);
void bind_descriptor_set(const VkCommandBuffer cmd, VkDescriptorSet descriptor_set);
};
}

View file

@ -250,7 +250,7 @@ namespace vk
CHECK_RESULT(vkCreateGraphicsPipelines(*m_device, nullptr, 1, &info, NULL, &pipeline));
auto program = std::make_unique<vk::glsl::program>(*m_device, pipeline, get_vertex_inputs(), get_fragment_inputs());
auto program = std::make_unique<vk::glsl::program>(*m_device, pipeline, m_pipeline_layout, get_vertex_inputs(), get_fragment_inputs());
auto result = program.get();
m_program_cache[storage_key] = std::move(program);

View file

@ -217,6 +217,13 @@ void VKGSRender::frame_context_cleanup(vk::frame_context_t *ctx, bool free_resou
ctx->buffer_views_to_clean.clear();
if (g_cfg.video.shader_interpreter_mode != shader_interpreter_mode::disabled)
{
// TODO: This is jank AF
m_vertex_instructions_buffer.reset_allocation_stats();
m_fragment_instructions_buffer.reset_allocation_stats();
}
if (ctx->last_frame_sync_time > m_last_heap_sync_time)
{
m_last_heap_sync_time = ctx->last_frame_sync_time;

View file

@ -178,7 +178,7 @@ struct VKTraits
CHECK_RESULT(vkCreateGraphicsPipelines(dev, nullptr, 1, &info, NULL, &pipeline));
pipeline_storage_type result = std::make_unique<vk::glsl::program>(dev, pipeline, vertexProgramData.uniforms, fragmentProgramData.uniforms);
pipeline_storage_type result = std::make_unique<vk::glsl::program>(dev, pipeline, common_pipeline_layout, vertexProgramData.uniforms, fragmentProgramData.uniforms);
result->link();
return result;
}

View file

@ -20,16 +20,16 @@ namespace vk
vs_texture_bindings.fill(~0u);
}
program::program(VkDevice dev, VkPipeline p, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs)
: m_device(dev), pipeline(p)
program::program(VkDevice dev, VkPipeline p, VkPipelineLayout layout, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs)
: m_device(dev), pipeline(p), pipeline_layout(layout)
{
create_impl();
load_uniforms(vertex_input);
load_uniforms(fragment_inputs);
}
program::program(VkDevice dev, VkPipeline p)
: m_device(dev), pipeline(p)
program::program(VkDevice dev, VkPipeline p, VkPipelineLayout layout)
: m_device(dev), pipeline(p), pipeline_layout(layout)
{
create_impl();
}

View file

@ -1,7 +1,493 @@
#include "stdafx.h"
#include "stdafx.h"
#include "VKShaderInterpreter.h"
#include "VKVertexProgram.h"
#include "VKFragmentProgram.h"
#include "VKGSRender.h"
#include "../Common/GLSLCommon.h"
#include "../Common/ShaderInterpreter.h"
namespace vk
{
void shader_interpreter::build_vs()
{
::glsl::shader_properties properties{};
properties.domain = ::glsl::program_domain::glsl_vertex_program;
properties.require_lit_emulation = true;
// TODO: Extend decompiler thread
// TODO: Rename decompiler thread, it no longer spawns a thread
RSXVertexProgram null_prog;
std::string shader_str;
ParamArray arr;
VKVertexProgram vk_prog;
VKVertexDecompilerThread comp(null_prog, shader_str, arr, vk_prog);
std::stringstream builder;
comp.insertHeader(builder);
comp.insertConstants(builder, {});
comp.insertInputs(builder, {});
// Insert vp stream input
builder << "\n"
"layout(std140, set=0, binding=" << m_vertex_instruction_start << ") readonly restrict buffer VertexInstructionBlock\n"
"{\n"
" uint base_address;\n"
" uint entry;\n"
" uint output_mask;\n"
" uint reserved;\n"
" uvec4 vp_instructions[];\n"
"};\n\n";
::glsl::insert_glsl_legacy_function(builder, properties);
::glsl::insert_vertex_input_fetch(builder, ::glsl::glsl_rules::glsl_rules_spirv);
builder << program_common::interpreter::get_vertex_interpreter();
const std::string s = builder.str();
m_vs.create(::glsl::program_domain::glsl_vertex_program, s);
m_vs.compile();
// Prepare input table
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
vk::glsl::program_input in;
in.location = binding_table.vertex_params_bind_slot;;
in.domain = ::glsl::glsl_vertex_program;
in.name = "VertexContextBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_buffers_first_bind_slot;
in.name = "persistent_input_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_buffers_first_bind_slot + 1;
in.name = "volatile_input_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_buffers_first_bind_slot + 2;
in.name = "vertex_layout_stream";
in.type = vk::glsl::input_type_texel_buffer;
m_vs_inputs.push_back(in);
in.location = binding_table.vertex_constant_buffers_bind_slot;
in.name = "VertexConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
m_vs_inputs.push_back(in);
// TODO: Bind textures if needed
}
void shader_interpreter::build_fs()
{
::glsl::shader_properties properties{};
properties.domain = ::glsl::program_domain::glsl_fragment_program;
properties.require_depth_conversion = true;
properties.require_wpos = true;
u32 len;
ParamArray arr;
std::string shader_str;
RSXFragmentProgram frag;
VKFragmentProgram vk_prog;
VKFragmentDecompilerThread comp(shader_str, arr, frag, len, vk_prog);
std::stringstream builder;
builder <<
"#version 450\n"
"#extension GL_ARB_separate_shader_objects : enable\n\n";
::glsl::insert_subheader_block(builder);
comp.insertConstants(builder);
const char* type_names[] = { "sampler1D", "sampler2D", "sampler3D", "samplerCube" };
for (int i = 0, bind_location = m_fragment_textures_start; i < 4; ++i)
{
builder << "layout(set=0, binding=" << bind_location++ << ") " << "uniform " << type_names[i] << " " << type_names[i] << "_array[16];\n";
}
builder << "\n"
"#define IS_TEXTURE_RESIDENT(index) true\n"
"#define SAMPLER1D(index) sampler1D_array[index]\n"
"#define SAMPLER2D(index) sampler2D_array[index]\n"
"#define SAMPLER3D(index) sampler3D_array[index]\n"
"#define SAMPLERCUBE(index) samplerCube_array[index]\n\n";
builder <<
"layout(std430, binding=" << m_fragment_instruction_start << ") readonly restrict buffer FragmentInstructionBlock\n"
"{\n"
" uint shader_control;\n"
" uint texture_control;\n"
" uint reserved1;\n"
" uint reserved2;\n"
" uvec4 fp_instructions[];\n"
"};\n\n";
::program_common::insert_fog_declaration(builder, "vec4", "fogc", true);
builder << program_common::interpreter::get_fragment_interpreter();
const std::string s = builder.str();
m_fs.create(::glsl::program_domain::glsl_fragment_program, s);
m_fs.compile();
// Prepare input table
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
vk::glsl::program_input in;
in.location = binding_table.fragment_constant_buffers_bind_slot;
in.domain = ::glsl::glsl_fragment_program;
in.name = "FragmentConstantsBuffer";
in.type = vk::glsl::input_type_uniform_buffer;
m_fs_inputs.push_back(in);
in.location = binding_table.fragment_state_bind_slot;
in.name = "FragmentStateBuffer";
m_fs_inputs.push_back(in);
in.location = binding_table.fragment_texture_params_bind_slot;
in.name = "TextureParametersBuffer";
m_fs_inputs.push_back(in);
for (int i = 0, location = m_fragment_textures_start; i < 4; ++i, ++location)
{
in.location = location;
in.name = std::string(type_names[i]) + "_array[16]";
m_fs_inputs.push_back(in);
}
}
std::pair<VkDescriptorSetLayout, VkPipelineLayout> shader_interpreter::create_layout(VkDevice dev)
{
const auto& binding_table = vk::get_current_renderer()->get_pipeline_binding_table();
std::vector<VkDescriptorSetLayoutBinding> bindings(binding_table.total_descriptor_bindings);
uint32_t idx = 0;
// Vertex stream, one stream for cacheable data, one stream for transient data. Third stream contains vertex layout info
for (int i = 0; i < 3; i++)
{
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_buffers_first_bind_slot + i;
idx++;
}
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_constant_buffers_bind_slot;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_state_bind_slot;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.fragment_texture_params_bind_slot;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.vertex_constant_buffers_bind_slot;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
bindings[idx].binding = binding_table.vertex_params_bind_slot;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.conditional_render_predicate_slot;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot;
m_fragment_textures_start = bindings[idx].binding;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 1;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 2;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 16;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 3;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[idx].descriptorCount = 4;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 4;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 5;
m_vertex_instruction_start = bindings[idx].binding;
idx++;
bindings[idx].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
bindings[idx].descriptorCount = 1;
bindings[idx].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings[idx].binding = binding_table.textures_first_bind_slot + 6;
m_fragment_instruction_start = bindings[idx].binding;
idx++;
bindings.resize(idx);
std::array<VkPushConstantRange, 1> push_constants;
push_constants[0].offset = 0;
push_constants[0].size = 16;
push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
if (vk::emulate_conditional_rendering())
{
// Conditional render toggle
push_constants[0].size = 20;
}
VkDescriptorSetLayoutCreateInfo infos = {};
infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
infos.pBindings = bindings.data();
infos.bindingCount = static_cast<uint32_t>(bindings.size());
VkDescriptorSetLayout set_layout;
CHECK_RESULT(vkCreateDescriptorSetLayout(dev, &infos, nullptr, &set_layout));
VkPipelineLayoutCreateInfo layout_info = {};
layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
layout_info.setLayoutCount = 1;
layout_info.pSetLayouts = &set_layout;
layout_info.pushConstantRangeCount = 1;
layout_info.pPushConstantRanges = push_constants.data();
VkPipelineLayout result;
CHECK_RESULT(vkCreatePipelineLayout(dev, &layout_info, nullptr, &result));
return { set_layout, result };
}
void shader_interpreter::create_descriptor_pools(const vk::render_device& dev)
{
std::vector<VkDescriptorPoolSize> sizes;
sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 6 * DESCRIPTOR_MAX_DRAW_CALLS });
sizes.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 3 * DESCRIPTOR_MAX_DRAW_CALLS });
sizes.push_back({ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 68 * DESCRIPTOR_MAX_DRAW_CALLS });
sizes.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3 * DESCRIPTOR_MAX_DRAW_CALLS });
m_descriptor_pool.create(dev, sizes.data(), ::size32(sizes), DESCRIPTOR_MAX_DRAW_CALLS, 2);
}
void shader_interpreter::init(const vk::render_device& dev)
{
m_device = dev;
std::tie(m_shared_descriptor_layout, m_shared_pipeline_layout) = create_layout(dev);
create_descriptor_pools(dev);
build_vs();
build_fs();
// TODO: Seed the cache
}
void shader_interpreter::destroy()
{
m_vs.destroy();
m_fs.destroy();
m_program_cache.clear();
m_descriptor_pool.destroy();
if (m_shared_pipeline_layout)
{
vkDestroyPipelineLayout(m_device, m_shared_pipeline_layout, nullptr);
m_shared_pipeline_layout = VK_NULL_HANDLE;
}
if (m_shared_descriptor_layout)
{
vkDestroyDescriptorSetLayout(m_device, m_shared_descriptor_layout, nullptr);
m_shared_descriptor_layout = VK_NULL_HANDLE;
}
}
glsl::program* shader_interpreter::link(const vk::pipeline_props& properties)
{
VkPipelineShaderStageCreateInfo shader_stages[2] = {};
shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
shader_stages[0].module = m_vs.get_handle();
shader_stages[0].pName = "main";
shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
shader_stages[1].module = m_fs.get_handle();
shader_stages[1].pName = "main";
VkDynamicState dynamic_state_descriptors[VK_DYNAMIC_STATE_RANGE_SIZE] = {};
VkPipelineDynamicStateCreateInfo dynamic_state_info = {};
dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_LINE_WIDTH;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BIAS;
if (vk::get_current_renderer()->get_depth_bounds_support())
{
dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BOUNDS;
}
dynamic_state_info.pDynamicStates = dynamic_state_descriptors;
VkPipelineVertexInputStateCreateInfo vi = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
VkPipelineViewportStateCreateInfo vp = {};
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vp.viewportCount = 1;
vp.scissorCount = 1;
VkPipelineMultisampleStateCreateInfo ms = properties.state.ms;
verify("Multisample state mismatch!" HERE), ms.rasterizationSamples == VkSampleCountFlagBits((properties.renderpass_key >> 16) & 0xF);
if (ms.rasterizationSamples != VK_SAMPLE_COUNT_1_BIT)
{
// Update the sample mask pointer
ms.pSampleMask = &properties.state.temp_storage.msaa_sample_mask;
}
// Rebase pointers from pipeline structure in case it is moved/copied
VkPipelineColorBlendStateCreateInfo cs = properties.state.cs;
cs.pAttachments = properties.state.att_state;
VkPipeline pipeline;
VkGraphicsPipelineCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
info.pVertexInputState = &vi;
info.pInputAssemblyState = &properties.state.ia;
info.pRasterizationState = &properties.state.rs;
info.pColorBlendState = &cs;
info.pMultisampleState = &ms;
info.pViewportState = &vp;
info.pDepthStencilState = &properties.state.ds;
info.stageCount = 2;
info.pStages = shader_stages;
info.pDynamicState = &dynamic_state_info;
info.layout = m_shared_pipeline_layout;
info.basePipelineIndex = -1;
info.basePipelineHandle = VK_NULL_HANDLE;
info.renderPass = vk::get_renderpass(m_device, properties.renderpass_key);
CHECK_RESULT(vkCreateGraphicsPipelines(m_device, nullptr, 1, &info, NULL, &pipeline));
return new vk::glsl::program(m_device, pipeline, m_shared_pipeline_layout, m_vs_inputs, m_fs_inputs);
}
void shader_interpreter::update_fragment_textures(const std::array<VkDescriptorImageInfo, 68>& sampled_images, VkDescriptorSet descriptor_set)
{
const VkDescriptorImageInfo* texture_ptr = sampled_images.data();
for (uint32_t i = 0, binding = m_fragment_textures_start; i < 4; ++i, ++binding, texture_ptr += 16)
{
const VkWriteDescriptorSet descriptor_writer =
{
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, // sType
nullptr, // pNext
descriptor_set, // dstSet
binding, // dstBinding
0, // dstArrayElement
16, // descriptorCount
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // descriptorType
texture_ptr, // pImageInfo
nullptr, // pBufferInfo
nullptr // pTexelBufferView
};
vkUpdateDescriptorSets(m_device, 1, &descriptor_writer, 0, nullptr);
}
}
VkDescriptorSet shader_interpreter::allocate_descriptor_set()
{
if (m_used_descriptors == DESCRIPTOR_MAX_DRAW_CALLS)
{
m_descriptor_pool.reset(0);
m_used_descriptors = 0;
}
VkDescriptorSetAllocateInfo alloc_info = {};
alloc_info.descriptorPool = m_descriptor_pool;
alloc_info.descriptorSetCount = 1;
alloc_info.pSetLayouts = &m_shared_descriptor_layout;
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
VkDescriptorSet new_descriptor_set;
CHECK_RESULT(vkAllocateDescriptorSets(m_device, &alloc_info, &new_descriptor_set));
m_used_descriptors++;
return new_descriptor_set;
}
glsl::program* shader_interpreter::get(const vk::pipeline_props& properties)
{
auto found = m_program_cache.find(properties);
if (found != m_program_cache.end()) [[likely]]
{
m_current_interpreter = found->second.get();
return m_current_interpreter;
}
m_current_interpreter = link(properties);
m_program_cache[properties].reset(m_current_interpreter);
return m_current_interpreter;
}
bool shader_interpreter::is_interpreter(const glsl::program* prog) const
{
return prog == m_current_interpreter;
}
uint32_t shader_interpreter::get_vertex_instruction_location() const
{
return m_vertex_instruction_start;
}
uint32_t shader_interpreter::get_fragment_instruction_location() const
{
return m_fragment_instruction_start;
}
};

View file

@ -1,9 +1,58 @@
#pragma once
#include "VKGSRender.h"
#pragma once
#include "VKProgramBuffer.h"
namespace vk
{
class shader_interpreter : glsl::program
using ::program_hash_util::fragment_program_utils;
using ::program_hash_util::vertex_program_utils;
class shader_interpreter
{
glsl::shader m_vs;
glsl::shader m_fs;
std::vector<glsl::program_input> m_vs_inputs;
std::vector<glsl::program_input> m_fs_inputs;
VkDevice m_device = VK_NULL_HANDLE;
VkDescriptorSetLayout m_shared_descriptor_layout = VK_NULL_HANDLE;
VkPipelineLayout m_shared_pipeline_layout = VK_NULL_HANDLE;
glsl::program* m_current_interpreter = nullptr;
struct key_hasher
{
size_t operator()(const vk::pipeline_props& props) const
{
return rpcs3::hash_struct(props);
}
};
std::unordered_map<vk::pipeline_props, std::unique_ptr<glsl::program>, key_hasher> m_program_cache;
vk::descriptor_pool m_descriptor_pool;
size_t m_used_descriptors = 0;
uint32_t m_vertex_instruction_start = 0;
uint32_t m_fragment_instruction_start = 0;
uint32_t m_fragment_textures_start = 0;
std::pair<VkDescriptorSetLayout, VkPipelineLayout> create_layout(VkDevice dev);
void create_descriptor_pools(const vk::render_device& dev);
void build_vs();
void build_fs();
glsl::program* link(const vk::pipeline_props& properties);
public:
void init(const vk::render_device& dev);
void destroy();
glsl::program* get(const vk::pipeline_props& properties);
bool is_interpreter(const glsl::program* prog) const;
uint32_t get_vertex_instruction_location() const;
uint32_t get_fragment_instruction_location() const;
void update_fragment_textures(const std::array<VkDescriptorImageInfo, 68>& sampled_images, VkDescriptorSet descriptor_set);
VkDescriptorSet allocate_descriptor_set();
};
}

View file

@ -196,7 +196,7 @@ namespace vk
CHECK_RESULT(vkCreateGraphicsPipelines(dev, nullptr, 1, &info, NULL, &pipeline));
const std::vector<vk::glsl::program_input> unused;
m_program = std::make_unique<vk::glsl::program>(static_cast<VkDevice>(dev), pipeline, unused, unused);
m_program = std::make_unique<vk::glsl::program>(static_cast<VkDevice>(dev), pipeline, m_pipeline_layout, unused, unused);
}
void load_program(vk::command_buffer &cmd, float scale_x, float scale_y, const float *offsets, size_t nb_offsets, std::array<float, 4> color)

View file

@ -5,8 +5,15 @@
#include "VulkanAPI.h"
#include "../VK/VKHelpers.h"
namespace vk
{
class shader_interpreter;
}
struct VKVertexDecompilerThread : public VertexProgramDecompiler
{
friend class vk::shader_interpreter;
std::string &m_shader;
std::vector<vk::glsl::program_input> inputs;
class VKVertexProgram *vk_prog;