mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-04 05:51:27 +12:00
rsx: Enable MSAA
- vk: Enable depth buffer resolve+unresolve - vk: Add AMD stenciling extension support - rsx: Temporarily disables MSAA-compatible hacks such as transparency AA - TODO: Add paths to optionally disable MSAA
This commit is contained in:
parent
f6f3b40ecc
commit
4a5bbba277
20 changed files with 1214 additions and 181 deletions
|
@ -38,7 +38,8 @@ namespace rsx
|
||||||
enum surface_access : u32
|
enum surface_access : u32
|
||||||
{
|
{
|
||||||
read = 0,
|
read = 0,
|
||||||
write = 1
|
write = 1,
|
||||||
|
transfer = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
//Sampled image descriptor
|
//Sampled image descriptor
|
||||||
|
|
|
@ -677,11 +677,11 @@ namespace rsx
|
||||||
{
|
{
|
||||||
for (auto &tex_info : data)
|
for (auto &tex_info : data)
|
||||||
{
|
{
|
||||||
auto this_address = std::get<0>(tex_info);
|
const auto this_address = tex_info.first;
|
||||||
if (this_address >= limit)
|
if (this_address >= limit)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto surface = std::get<1>(tex_info).get();
|
auto surface = tex_info.second.get();
|
||||||
const auto pitch = surface->get_rsx_pitch();
|
const auto pitch = surface->get_rsx_pitch();
|
||||||
if (!rsx::pitch_compatible(surface, required_pitch, required_height))
|
if (!rsx::pitch_compatible(surface, required_pitch, required_height))
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -11,7 +11,9 @@ namespace rsx
|
||||||
enum surface_state_flags : u32
|
enum surface_state_flags : u32
|
||||||
{
|
{
|
||||||
ready = 0,
|
ready = 0,
|
||||||
erase_bkgnd = 1
|
erase_bkgnd = 1,
|
||||||
|
require_resolve = 2,
|
||||||
|
require_unresolve = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename surface_type>
|
template <typename surface_type>
|
||||||
|
@ -119,8 +121,11 @@ namespace rsx
|
||||||
u8 samples_x = 1;
|
u8 samples_x = 1;
|
||||||
u8 samples_y = 1;
|
u8 samples_y = 1;
|
||||||
|
|
||||||
|
std::unique_ptr<typename std::remove_pointer<image_storage_type>::type> resolve_surface;
|
||||||
|
|
||||||
flags32_t memory_usage_flags = surface_usage_flags::unknown;
|
flags32_t memory_usage_flags = surface_usage_flags::unknown;
|
||||||
flags32_t state_flags = surface_state_flags::ready;
|
flags32_t state_flags = surface_state_flags::ready;
|
||||||
|
flags32_t msaa_flags = surface_state_flags::ready;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
|
@ -410,7 +415,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_write(u64 write_tag = 0)
|
void on_write(u64 write_tag = 0, rsx::surface_state_flags resolve_flags = surface_state_flags::require_resolve)
|
||||||
{
|
{
|
||||||
if (write_tag)
|
if (write_tag)
|
||||||
{
|
{
|
||||||
|
@ -424,12 +429,22 @@ namespace rsx
|
||||||
// HACK!! This should be cleared through memory barriers only
|
// HACK!! This should be cleared through memory barriers only
|
||||||
state_flags = rsx::surface_state_flags::ready;
|
state_flags = rsx::surface_state_flags::ready;
|
||||||
|
|
||||||
|
if (spp > 1)
|
||||||
|
{
|
||||||
|
msaa_flags = resolve_flags;
|
||||||
|
}
|
||||||
|
|
||||||
if (old_contents.source)
|
if (old_contents.source)
|
||||||
{
|
{
|
||||||
clear_rw_barrier();
|
clear_rw_barrier();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_write_copy(u64 write_tag = 0)
|
||||||
|
{
|
||||||
|
on_write(write_tag, rsx::surface_state_flags::require_unresolve);
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the rect area occupied by this surface expressed as an 8bpp image with no AA
|
// Returns the rect area occupied by this surface expressed as an 8bpp image with no AA
|
||||||
areau get_normalized_memory_area() const
|
areau get_normalized_memory_area() const
|
||||||
{
|
{
|
||||||
|
@ -456,6 +471,17 @@ namespace rsx
|
||||||
area.y2 /= samples_y;
|
area.y2 /= samples_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void transform_pixels_to_samples(area_base<T>& area)
|
||||||
|
{
|
||||||
|
if (LIKELY(spp == 1)) return;
|
||||||
|
|
||||||
|
area.x1 *= samples_x;
|
||||||
|
area.x2 *= samples_x;
|
||||||
|
area.y1 *= samples_y;
|
||||||
|
area.y2 *= samples_y;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void transform_samples_to_pixels(T& x1, T& x2, T& y1, T& y2)
|
void transform_samples_to_pixels(T& x1, T& x2, T& y1, T& y2)
|
||||||
{
|
{
|
||||||
|
@ -466,5 +492,16 @@ namespace rsx
|
||||||
y1 /= samples_y;
|
y1 /= samples_y;
|
||||||
y2 /= samples_y;
|
y2 /= samples_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void transform_pixels_to_samples(T& x1, T& x2, T& y1, T& y2)
|
||||||
|
{
|
||||||
|
if (LIKELY(spp == 1)) return;
|
||||||
|
|
||||||
|
x1 *= samples_x;
|
||||||
|
x2 *= samples_x;
|
||||||
|
y1 *= samples_y;
|
||||||
|
y2 *= samples_y;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2564,7 +2564,7 @@ namespace rsx
|
||||||
// Destination dimensions are relaxed (true)
|
// Destination dimensions are relaxed (true)
|
||||||
dst_area = dst_subres.get_src_area();
|
dst_area = dst_subres.get_src_area();
|
||||||
|
|
||||||
dest_texture = dst_subres.surface->get_surface(rsx::surface_access::write);
|
dest_texture = dst_subres.surface->get_surface(rsx::surface_access::transfer);
|
||||||
typeless_info.dst_context = texture_upload_context::framebuffer_storage;
|
typeless_info.dst_context = texture_upload_context::framebuffer_storage;
|
||||||
|
|
||||||
max_dst_width = (u16)(dst_subres.surface->get_surface_width(rsx::surface_metrics::samples) * typeless_info.dst_scaling_hint);
|
max_dst_width = (u16)(dst_subres.surface->get_surface_width(rsx::surface_metrics::samples) * typeless_info.dst_scaling_hint);
|
||||||
|
@ -2851,7 +2851,7 @@ namespace rsx
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dst_subres.surface->on_write(rsx::get_shared_tag());
|
dst_subres.surface->on_write_copy(rsx::get_shared_tag());
|
||||||
m_rtts.notify_memory_structure_changed();
|
m_rtts.notify_memory_structure_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2889,13 +2889,13 @@ namespace rsx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_is_render_target)
|
if (0)//src_is_render_target)
|
||||||
{
|
{
|
||||||
// TODO: Specify typeless for high sample counts
|
// TODO: Specify typeless for high sample counts
|
||||||
src_subres.surface->transform_samples_to_pixels(src_area);
|
src_subres.surface->transform_samples_to_pixels(src_area);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dst_is_render_target)
|
if (0)//dst_is_render_target)
|
||||||
{
|
{
|
||||||
// TODO: Specify typeless for high sample counts
|
// TODO: Specify typeless for high sample counts
|
||||||
dst_subres.surface->transform_samples_to_pixels(dst_area);
|
dst_subres.surface->transform_samples_to_pixels(dst_area);
|
||||||
|
|
|
@ -94,7 +94,7 @@ namespace gl
|
||||||
static_cast<gl::render_target*>(t)->release();
|
static_cast<gl::render_target*>(t)->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
texture* get_surface(rsx::surface_access access_type) override
|
texture* get_surface(rsx::surface_access /*access_type*/) override
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
return (gl::texture*)this;
|
return (gl::texture*)this;
|
||||||
|
@ -259,7 +259,6 @@ struct gl_render_target_traits
|
||||||
void invalidate_surface_contents(gl::command_context&, gl::render_target *surface, u32 address, size_t pitch)
|
void invalidate_surface_contents(gl::command_context&, gl::render_target *surface, u32 address, size_t pitch)
|
||||||
{
|
{
|
||||||
surface->set_rsx_pitch((u16)pitch);
|
surface->set_rsx_pitch((u16)pitch);
|
||||||
surface->set_aa_mode(rsx::surface_antialiasing::center_1_sample);
|
|
||||||
surface->queue_tag(address);
|
surface->queue_tag(address);
|
||||||
surface->last_use_tag = 0;
|
surface->last_use_tag = 0;
|
||||||
surface->memory_usage_flags = rsx::surface_usage_flags::unknown;
|
surface->memory_usage_flags = rsx::surface_usage_flags::unknown;
|
||||||
|
@ -278,7 +277,7 @@ struct gl_render_target_traits
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
void notify_surface_persist(const std::unique_ptr<gl::render_target>& surface)
|
void notify_surface_persist(const std::unique_ptr<gl::render_target>& /*surface*/)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
static
|
static
|
||||||
|
|
|
@ -658,8 +658,8 @@ namespace rsx
|
||||||
auto alpha_ref = rsx::method_registers.alpha_ref() / 255.f;
|
auto alpha_ref = rsx::method_registers.alpha_ref() / 255.f;
|
||||||
auto rop_control = rsx::method_registers.alpha_test_enabled()? 1u : 0u;
|
auto rop_control = rsx::method_registers.alpha_test_enabled()? 1u : 0u;
|
||||||
|
|
||||||
if (rsx::method_registers.msaa_alpha_to_coverage_enabled() &&
|
if (0 &&
|
||||||
rsx::method_registers.msaa_enabled() &&
|
rsx::method_registers.msaa_alpha_to_coverage_enabled() &&
|
||||||
rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample)
|
rsx::method_registers.surface_antialias() != rsx::surface_antialiasing::center_1_sample)
|
||||||
{
|
{
|
||||||
// Alpha values generate a coverage mask for order independent blending
|
// Alpha values generate a coverage mask for order independent blending
|
||||||
|
|
|
@ -25,35 +25,49 @@ namespace vk
|
||||||
u32 optimal_group_size = 1;
|
u32 optimal_group_size = 1;
|
||||||
u32 optimal_kernel_size = 1;
|
u32 optimal_kernel_size = 1;
|
||||||
|
|
||||||
|
virtual std::vector<std::pair<VkDescriptorType, u8>> get_descriptor_layout()
|
||||||
|
{
|
||||||
|
std::vector<std::pair<VkDescriptorType, u8>> result;
|
||||||
|
result.push_back({ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1 });
|
||||||
|
|
||||||
|
if (uniform_inputs)
|
||||||
|
{
|
||||||
|
result.push_back({ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void init_descriptors()
|
void init_descriptors()
|
||||||
{
|
{
|
||||||
VkDescriptorPoolSize descriptor_pool_sizes[2] =
|
std::vector<VkDescriptorPoolSize> descriptor_pool_sizes;
|
||||||
|
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||||
|
|
||||||
|
const auto layout = get_descriptor_layout();
|
||||||
|
for (const auto &e : layout)
|
||||||
{
|
{
|
||||||
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_MAX_COMPUTE_TASKS },
|
descriptor_pool_sizes.push_back({e.first, u32(VK_MAX_COMPUTE_TASKS * e.second)});
|
||||||
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_MAX_COMPUTE_TASKS }
|
|
||||||
};
|
for (unsigned n = 0; n < e.second; ++n)
|
||||||
|
{
|
||||||
|
bindings.push_back
|
||||||
|
({
|
||||||
|
uint32_t(bindings.size()),
|
||||||
|
e.first,
|
||||||
|
1,
|
||||||
|
VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
|
nullptr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Reserve descriptor pools
|
// Reserve descriptor pools
|
||||||
m_descriptor_pool.create(*get_current_renderer(), descriptor_pool_sizes, 2, VK_MAX_COMPUTE_TASKS, 2);
|
m_descriptor_pool.create(*get_current_renderer(), descriptor_pool_sizes.data(), (u32)descriptor_pool_sizes.size(), VK_MAX_COMPUTE_TASKS, 2);
|
||||||
|
|
||||||
std::vector<VkDescriptorSetLayoutBinding> bindings(2);
|
|
||||||
|
|
||||||
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
||||||
bindings[0].descriptorCount = 1;
|
|
||||||
bindings[0].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
||||||
bindings[0].binding = 0;
|
|
||||||
bindings[0].pImmutableSamplers = nullptr;
|
|
||||||
|
|
||||||
bindings[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
||||||
bindings[1].descriptorCount = 1;
|
|
||||||
bindings[1].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
||||||
bindings[1].binding = 1;
|
|
||||||
bindings[1].pImmutableSamplers = nullptr;
|
|
||||||
|
|
||||||
VkDescriptorSetLayoutCreateInfo infos = {};
|
VkDescriptorSetLayoutCreateInfo infos = {};
|
||||||
infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
infos.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||||
infos.pBindings = bindings.data();
|
infos.pBindings = bindings.data();
|
||||||
infos.bindingCount = uniform_inputs? 2u : 1u;
|
infos.bindingCount = (u32)bindings.size();
|
||||||
|
|
||||||
CHECK_RESULT(vkCreateDescriptorSetLayout(*get_current_renderer(), &infos, nullptr, &m_descriptor_layout));
|
CHECK_RESULT(vkCreateDescriptorSetLayout(*get_current_renderer(), &infos, nullptr, &m_descriptor_layout));
|
||||||
|
|
||||||
|
@ -120,6 +134,9 @@ namespace vk
|
||||||
virtual void bind_resources()
|
virtual void bind_resources()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
virtual void declare_inputs()
|
||||||
|
{}
|
||||||
|
|
||||||
void load_program(VkCommandBuffer cmd)
|
void load_program(VkCommandBuffer cmd)
|
||||||
{
|
{
|
||||||
if (!m_program)
|
if (!m_program)
|
||||||
|
@ -143,8 +160,8 @@ namespace vk
|
||||||
VkPipeline pipeline;
|
VkPipeline pipeline;
|
||||||
vkCreateComputePipelines(*get_current_renderer(), nullptr, 1, &info, nullptr, &pipeline);
|
vkCreateComputePipelines(*get_current_renderer(), nullptr, 1, &info, nullptr, &pipeline);
|
||||||
|
|
||||||
std::vector<vk::glsl::program_input> inputs;
|
m_program = std::make_unique<vk::glsl::program>(*get_current_renderer(), pipeline);
|
||||||
m_program = std::make_unique<vk::glsl::program>(*get_current_renderer(), pipeline, inputs, inputs);
|
declare_inputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(HERE), m_used_descriptors < VK_MAX_COMPUTE_TASKS;
|
verify(HERE), m_used_descriptors < VK_MAX_COMPUTE_TASKS;
|
||||||
|
@ -164,10 +181,15 @@ namespace vk
|
||||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline_layout, 0, 1, &m_descriptor_set, 0, nullptr);
|
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline_layout, 0, 1, &m_descriptor_set, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void run(VkCommandBuffer cmd, u32 num_invocations)
|
virtual void run(VkCommandBuffer cmd, u32 invocations_x, u32 invocations_y)
|
||||||
{
|
{
|
||||||
load_program(cmd);
|
load_program(cmd);
|
||||||
vkCmdDispatch(cmd, num_invocations, 1, 1);
|
vkCmdDispatch(cmd, invocations_x, invocations_y, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void run(VkCommandBuffer cmd, u32 num_invocations)
|
||||||
|
{
|
||||||
|
run(cmd, num_invocations, 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -89,10 +89,10 @@ namespace vk
|
||||||
return std::make_pair(VK_FORMAT_R32G32B32A32_SFLOAT, vk::default_component_map());
|
return std::make_pair(VK_FORMAT_R32G32B32A32_SFLOAT, vk::default_component_map());
|
||||||
|
|
||||||
case rsx::surface_color_format::x1r5g5b5_o1r5g5b5:
|
case rsx::surface_color_format::x1r5g5b5_o1r5g5b5:
|
||||||
return std::make_pair(VK_FORMAT_B8G8R8A8_UNORM, o_rgb);
|
return std::make_pair(VK_FORMAT_A1R5G5B5_UNORM_PACK16, o_rgb);
|
||||||
|
|
||||||
case rsx::surface_color_format::x1r5g5b5_z1r5g5b5:
|
case rsx::surface_color_format::x1r5g5b5_z1r5g5b5:
|
||||||
return std::make_pair(VK_FORMAT_B8G8R8A8_UNORM, z_rgb);
|
return std::make_pair(VK_FORMAT_A1R5G5B5_UNORM_PACK16, z_rgb);
|
||||||
|
|
||||||
case rsx::surface_color_format::b8:
|
case rsx::surface_color_format::b8:
|
||||||
{
|
{
|
||||||
|
@ -539,7 +539,7 @@ VKGSRender::VKGSRender() : GSRender()
|
||||||
else
|
else
|
||||||
m_vertex_cache = std::make_unique<vk::weak_vertex_cache>();
|
m_vertex_cache = std::make_unique<vk::weak_vertex_cache>();
|
||||||
|
|
||||||
m_shaders_cache = std::make_unique<vk::shader_cache>(*m_prog_buffer, "vulkan", "v1.7");
|
m_shaders_cache = std::make_unique<vk::shader_cache>(*m_prog_buffer, "vulkan", "v1.8");
|
||||||
|
|
||||||
open_command_buffer();
|
open_command_buffer();
|
||||||
|
|
||||||
|
@ -1566,7 +1566,11 @@ void VKGSRender::end()
|
||||||
if (!image_ptr)
|
if (!image_ptr)
|
||||||
{
|
{
|
||||||
LOG_ERROR(RSX, "Texture upload failed to vtexture index %d. Binding null sampler.", i);
|
LOG_ERROR(RSX, "Texture upload failed to vtexture index %d. Binding null sampler.", i);
|
||||||
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }, rsx::constants::vertex_texture_names[i], m_current_frame->descriptor_set);
|
m_program->bind_uniform({ vk::null_sampler(), vk::null_image_view(*m_current_command_buffer)->value, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL },
|
||||||
|
i,
|
||||||
|
::glsl::program_domain::glsl_vertex_program,
|
||||||
|
m_current_frame->descriptor_set);
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1623,16 +1627,6 @@ void VKGSRender::end()
|
||||||
m_current_command_buffer->flags |= vk::command_buffer::cb_has_occlusion_task;
|
m_current_command_buffer->flags |= vk::command_buffer::cb_has_occlusion_task;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final heap check...
|
|
||||||
check_heap_status(VK_HEAP_CHECK_VERTEX_STORAGE | VK_HEAP_CHECK_VERTEX_LAYOUT_STORAGE);
|
|
||||||
|
|
||||||
// While vertex upload is an interruptible process, if we made it this far, there's no need to sync anything that occurs past this point
|
|
||||||
// Only textures are synchronized tightly with the GPU and they have been read back above
|
|
||||||
vk::enter_uninterruptible();
|
|
||||||
|
|
||||||
vkCmdBindPipeline(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline);
|
|
||||||
update_draw_state();
|
|
||||||
|
|
||||||
// Apply write memory barriers
|
// Apply write memory barriers
|
||||||
if (true)//g_cfg.video.strict_rendering_mode)
|
if (true)//g_cfg.video.strict_rendering_mode)
|
||||||
{
|
{
|
||||||
|
@ -1682,6 +1676,16 @@ void VKGSRender::end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Final heap check...
|
||||||
|
check_heap_status(VK_HEAP_CHECK_VERTEX_STORAGE | VK_HEAP_CHECK_VERTEX_LAYOUT_STORAGE);
|
||||||
|
|
||||||
|
// While vertex upload is an interruptible process, if we made it this far, there's no need to sync anything that occurs past this point
|
||||||
|
// Only textures are synchronized tightly with the GPU and they have been read back above
|
||||||
|
vk::enter_uninterruptible();
|
||||||
|
|
||||||
|
vkCmdBindPipeline(*m_current_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline);
|
||||||
|
update_draw_state();
|
||||||
|
|
||||||
u32 sub_index = 0;
|
u32 sub_index = 0;
|
||||||
rsx::method_registers.current_draw_clause.begin();
|
rsx::method_registers.current_draw_clause.begin();
|
||||||
do
|
do
|
||||||
|
@ -2238,7 +2242,7 @@ void VKGSRender::frame_context_cleanup(frame_context_t *ctx, bool free_resources
|
||||||
m_overlay_manager->dispose(uids_to_dispose);
|
m_overlay_manager->dispose(uids_to_dispose);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::reset_compute_tasks();
|
vk::reset_global_resources();
|
||||||
|
|
||||||
m_attachment_clear_pass->free_resources();
|
m_attachment_clear_pass->free_resources();
|
||||||
m_depth_converter->free_resources();
|
m_depth_converter->free_resources();
|
||||||
|
@ -2472,8 +2476,18 @@ bool VKGSRender::load_program()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto rasterization_samples = u8((m_current_renderpass_key >> 16) & 0xF);
|
||||||
|
if (rasterization_samples > 1)
|
||||||
|
{
|
||||||
|
properties.state.set_multisample_state(
|
||||||
|
rasterization_samples,
|
||||||
|
rsx::method_registers.msaa_sample_mask(),
|
||||||
|
rsx::method_registers.msaa_enabled(),
|
||||||
|
rsx::method_registers.msaa_alpha_to_coverage_enabled(),
|
||||||
|
rsx::method_registers.msaa_alpha_to_one_enabled());
|
||||||
|
}
|
||||||
|
|
||||||
properties.renderpass_key = m_current_renderpass_key;
|
properties.renderpass_key = m_current_renderpass_key;
|
||||||
properties.num_targets = (u32)m_draw_buffers.size();
|
|
||||||
|
|
||||||
vk::enter_uninterruptible();
|
vk::enter_uninterruptible();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "VKCompute.h"
|
#include "VKCompute.h"
|
||||||
#include "VKRenderPass.h"
|
#include "VKRenderPass.h"
|
||||||
#include "VKFramebuffer.h"
|
#include "VKFramebuffer.h"
|
||||||
|
#include "VKResolveHelper.h"
|
||||||
#include "Utilities/mutex.h"
|
#include "Utilities/mutex.h"
|
||||||
|
|
||||||
namespace vk
|
namespace vk
|
||||||
|
@ -234,11 +235,18 @@ namespace vk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset_global_resources()
|
||||||
|
{
|
||||||
|
vk::reset_compute_tasks();
|
||||||
|
vk::reset_resolve_resources();
|
||||||
|
}
|
||||||
|
|
||||||
void destroy_global_resources()
|
void destroy_global_resources()
|
||||||
{
|
{
|
||||||
VkDevice dev = *g_current_renderer;
|
VkDevice dev = *g_current_renderer;
|
||||||
vk::clear_renderpass_cache(dev);
|
vk::clear_renderpass_cache(dev);
|
||||||
vk::clear_framebuffer_cache();
|
vk::clear_framebuffer_cache();
|
||||||
|
vk::clear_resolve_helpers();
|
||||||
|
|
||||||
g_null_texture.reset();
|
g_null_texture.reset();
|
||||||
g_null_image_view.reset();
|
g_null_image_view.reset();
|
||||||
|
@ -416,6 +424,27 @@ namespace vk
|
||||||
vkCmdPipelineBarrier(cmd, src_stage, dst_stage, 0, 0, nullptr, 1, &barrier, 0, nullptr);
|
vkCmdPipelineBarrier(cmd, src_stage, dst_stage, 0, 0, nullptr, 1, &barrier, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void insert_image_memory_barrier(
|
||||||
|
VkCommandBuffer cmd, VkImage image,
|
||||||
|
VkImageLayout current_layout, VkImageLayout new_layout,
|
||||||
|
VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage,
|
||||||
|
VkAccessFlags src_mask, VkAccessFlags dst_mask,
|
||||||
|
const VkImageSubresourceRange& range)
|
||||||
|
{
|
||||||
|
VkImageMemoryBarrier barrier = {};
|
||||||
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
barrier.newLayout = new_layout;
|
||||||
|
barrier.oldLayout = current_layout;
|
||||||
|
barrier.image = image;
|
||||||
|
barrier.srcAccessMask = src_mask;
|
||||||
|
barrier.dstAccessMask = dst_mask;
|
||||||
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.subresourceRange = range;
|
||||||
|
|
||||||
|
vkCmdPipelineBarrier(cmd, src_stage, dst_stage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||||
|
}
|
||||||
|
|
||||||
void change_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout current_layout, VkImageLayout new_layout, const VkImageSubresourceRange& range)
|
void change_image_layout(VkCommandBuffer cmd, VkImage image, VkImageLayout current_layout, VkImageLayout new_layout, const VkImageSubresourceRange& range)
|
||||||
{
|
{
|
||||||
//Prepare an image to match the new layout..
|
//Prepare an image to match the new layout..
|
||||||
|
@ -615,6 +644,12 @@ namespace vk
|
||||||
|
|
||||||
void insert_texture_barrier(VkCommandBuffer cmd, vk::image *image, VkImageLayout new_layout)
|
void insert_texture_barrier(VkCommandBuffer cmd, vk::image *image, VkImageLayout new_layout)
|
||||||
{
|
{
|
||||||
|
if (image->samples() > 1)
|
||||||
|
{
|
||||||
|
// This barrier is pointless for multisampled images as they require a resolve operation before access anyway
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
insert_texture_barrier(cmd, image->value, image->current_layout, new_layout, { image->aspect(), 0, 1, 0, 1 });
|
insert_texture_barrier(cmd, image->value, image->current_layout, new_layout, { image->aspect(), 0, 1, 0, 1 });
|
||||||
image->current_layout = new_layout;
|
image->current_layout = new_layout;
|
||||||
}
|
}
|
||||||
|
@ -835,6 +870,8 @@ namespace vk
|
||||||
{
|
{
|
||||||
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
|
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
|
||||||
{
|
{
|
||||||
|
if (strstr(pMsg, "IMAGE_VIEW_TYPE_1D")) return false;
|
||||||
|
|
||||||
LOG_ERROR(RSX, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg);
|
LOG_ERROR(RSX, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, pMsg);
|
||||||
}
|
}
|
||||||
else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
|
else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
|
||||||
|
|
|
@ -133,6 +133,7 @@ namespace vk
|
||||||
void reset_compute_tasks();
|
void reset_compute_tasks();
|
||||||
|
|
||||||
void destroy_global_resources();
|
void destroy_global_resources();
|
||||||
|
void reset_global_resources();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allocate enough space in upload_buffer and write all mipmap/layer data into the subbuffer.
|
* Allocate enough space in upload_buffer and write all mipmap/layer data into the subbuffer.
|
||||||
|
@ -172,6 +173,10 @@ namespace vk
|
||||||
void insert_buffer_memory_barrier(VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize length,
|
void insert_buffer_memory_barrier(VkCommandBuffer cmd, VkBuffer buffer, VkDeviceSize offset, VkDeviceSize length,
|
||||||
VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage, VkAccessFlags src_mask, VkAccessFlags dst_mask);
|
VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage, VkAccessFlags src_mask, VkAccessFlags dst_mask);
|
||||||
|
|
||||||
|
void insert_image_memory_barrier(VkCommandBuffer cmd, VkImage image, VkImageLayout current_layout, VkImageLayout new_layout,
|
||||||
|
VkPipelineStageFlags src_stage, VkPipelineStageFlags dst_stage, VkAccessFlags src_mask, VkAccessFlags dst_mask,
|
||||||
|
const VkImageSubresourceRange& range);
|
||||||
|
|
||||||
//Manage 'uininterruptible' state where secondary operations (e.g violation handlers) will have to wait
|
//Manage 'uininterruptible' state where secondary operations (e.g violation handlers) will have to wait
|
||||||
void enter_uninterruptible();
|
void enter_uninterruptible();
|
||||||
void leave_uninterruptible();
|
void leave_uninterruptible();
|
||||||
|
@ -1217,7 +1222,7 @@ namespace vk
|
||||||
public:
|
public:
|
||||||
using image::image;
|
using image::image;
|
||||||
|
|
||||||
image_view* get_view(u32 remap_encoding, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& remap,
|
virtual image_view* get_view(u32 remap_encoding, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& remap,
|
||||||
VkImageAspectFlags mask = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT)
|
VkImageAspectFlags mask = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT)
|
||||||
{
|
{
|
||||||
auto found = views.equal_range(remap_encoding);
|
auto found = views.equal_range(remap_encoding);
|
||||||
|
@ -2799,6 +2804,13 @@ public:
|
||||||
VkPipelineColorBlendAttachmentState att_state[4];
|
VkPipelineColorBlendAttachmentState att_state[4];
|
||||||
VkPipelineColorBlendStateCreateInfo cs;
|
VkPipelineColorBlendStateCreateInfo cs;
|
||||||
VkPipelineRasterizationStateCreateInfo rs;
|
VkPipelineRasterizationStateCreateInfo rs;
|
||||||
|
VkPipelineMultisampleStateCreateInfo ms;
|
||||||
|
|
||||||
|
struct extra_parameters
|
||||||
|
{
|
||||||
|
VkSampleMask msaa_sample_mask;
|
||||||
|
}
|
||||||
|
temp_storage;
|
||||||
|
|
||||||
graphics_pipeline_state()
|
graphics_pipeline_state()
|
||||||
{
|
{
|
||||||
|
@ -2814,6 +2826,10 @@ public:
|
||||||
rs.cullMode = VK_CULL_MODE_NONE;
|
rs.cullMode = VK_CULL_MODE_NONE;
|
||||||
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||||
rs.lineWidth = 1.f;
|
rs.lineWidth = 1.f;
|
||||||
|
|
||||||
|
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||||
|
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
temp_storage.msaa_sample_mask = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics_pipeline_state(const graphics_pipeline_state& other)
|
graphics_pipeline_state(const graphics_pipeline_state& other)
|
||||||
|
@ -2973,6 +2989,27 @@ public:
|
||||||
cs.attachmentCount = count;
|
cs.attachmentCount = count;
|
||||||
cs.pAttachments = att_state;
|
cs.pAttachments = att_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_multisample_state(u8 sample_count, u32 sample_mask, bool msaa_enabled, bool alpha_to_coverage, bool alpha_to_one)
|
||||||
|
{
|
||||||
|
temp_storage.msaa_sample_mask = sample_mask;
|
||||||
|
|
||||||
|
ms.rasterizationSamples = static_cast<VkSampleCountFlagBits>(sample_count);
|
||||||
|
ms.alphaToCoverageEnable = alpha_to_coverage;
|
||||||
|
ms.alphaToOneEnable = alpha_to_one;
|
||||||
|
|
||||||
|
if (!msaa_enabled)
|
||||||
|
{
|
||||||
|
// This register is likely glMinSampleShading but in reverse; probably sets max sample shading rate of 1
|
||||||
|
// I (kd-11) suspect its what the control panel setting affects when MSAA is set to disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_multisample_shading_rate(float shading_rate)
|
||||||
|
{
|
||||||
|
ms.sampleShadingEnable = VK_TRUE;
|
||||||
|
ms.minSampleShading = shading_rate;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace glsl
|
namespace glsl
|
||||||
|
@ -3095,21 +3132,24 @@ public:
|
||||||
std::array<u32, 4> vs_texture_bindings;
|
std::array<u32, 4> vs_texture_bindings;
|
||||||
bool linked;
|
bool linked;
|
||||||
|
|
||||||
|
void create_impl();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VkPipeline pipeline;
|
VkPipeline pipeline;
|
||||||
u64 attribute_location_mask;
|
u64 attribute_location_mask;
|
||||||
u64 vertex_attributes_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, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs);
|
||||||
|
program(VkDevice dev, VkPipeline p);
|
||||||
program(const program&) = delete;
|
program(const program&) = delete;
|
||||||
program(program&& other) = delete;
|
program(program&& other) = delete;
|
||||||
~program();
|
~program();
|
||||||
|
|
||||||
program& load_uniforms(::glsl::program_domain domain, const std::vector<program_input>& inputs);
|
program& load_uniforms(const std::vector<program_input>& inputs);
|
||||||
program& link();
|
program& link();
|
||||||
|
|
||||||
bool has_uniform(program_input_type type, const std::string &uniform_name);
|
bool has_uniform(program_input_type type, const std::string &uniform_name);
|
||||||
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorSet &descriptor_set);
|
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string &uniform_name, VkDescriptorType type, VkDescriptorSet &descriptor_set);
|
||||||
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, VkDescriptorSet &descriptor_set, bool is_stencil_mirror = false);
|
void bind_uniform(const VkDescriptorImageInfo &image_descriptor, int texture_unit, ::glsl::program_domain domain, VkDescriptorSet &descriptor_set, bool is_stencil_mirror = false);
|
||||||
void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, uint32_t binding_point, VkDescriptorSet &descriptor_set);
|
void bind_uniform(const VkDescriptorBufferInfo &buffer_descriptor, uint32_t binding_point, VkDescriptorSet &descriptor_set);
|
||||||
void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, VkDescriptorSet &descriptor_set);
|
void bind_uniform(const VkBufferView &buffer_view, program_input_type type, const std::string &binding_name, VkDescriptorSet &descriptor_set);
|
||||||
|
|
|
@ -80,7 +80,7 @@ namespace vk
|
||||||
|
|
||||||
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
bindings[0].descriptorCount = 1;
|
bindings[0].descriptorCount = 1;
|
||||||
bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
|
bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
bindings[0].binding = 0;
|
bindings[0].binding = 0;
|
||||||
bindings[0].pImmutableSamplers = nullptr;
|
bindings[0].pImmutableSamplers = nullptr;
|
||||||
|
|
||||||
|
@ -187,11 +187,6 @@ namespace vk
|
||||||
vp.scissorCount = 1;
|
vp.scissorCount = 1;
|
||||||
vp.viewportCount = 1;
|
vp.viewportCount = 1;
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo ms = {};
|
|
||||||
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
||||||
ms.pSampleMask = NULL;
|
|
||||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
||||||
|
|
||||||
VkPipeline pipeline;
|
VkPipeline pipeline;
|
||||||
VkGraphicsPipelineCreateInfo info = {};
|
VkGraphicsPipelineCreateInfo info = {};
|
||||||
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||||
|
@ -199,7 +194,7 @@ namespace vk
|
||||||
info.pInputAssemblyState = &renderpass_config.ia;
|
info.pInputAssemblyState = &renderpass_config.ia;
|
||||||
info.pRasterizationState = &renderpass_config.rs;
|
info.pRasterizationState = &renderpass_config.rs;
|
||||||
info.pColorBlendState = &renderpass_config.cs;
|
info.pColorBlendState = &renderpass_config.cs;
|
||||||
info.pMultisampleState = &ms;
|
info.pMultisampleState = &renderpass_config.ms;
|
||||||
info.pViewportState = &vp;
|
info.pViewportState = &vp;
|
||||||
info.pDepthStencilState = &renderpass_config.ds;
|
info.pDepthStencilState = &renderpass_config.ds;
|
||||||
info.stageCount = 2;
|
info.stageCount = 2;
|
||||||
|
@ -253,7 +248,7 @@ namespace vk
|
||||||
for (int n = 0; n < src.size(); ++n)
|
for (int n = 0; n < src.size(); ++n)
|
||||||
{
|
{
|
||||||
VkDescriptorImageInfo info = { m_sampler->value, src[n]->value, src[n]->image()->current_layout };
|
VkDescriptorImageInfo info = { m_sampler->value, src[n]->value, src[n]->image()->current_layout };
|
||||||
program->bind_uniform(info, "fs" + std::to_string(n), m_descriptor_set);
|
program->bind_uniform(info, "fs" + std::to_string(n), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, m_descriptor_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, program->pipeline);
|
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, program->pipeline);
|
||||||
|
|
|
@ -11,36 +11,38 @@ namespace vk
|
||||||
struct pipeline_props
|
struct pipeline_props
|
||||||
{
|
{
|
||||||
graphics_pipeline_state state;
|
graphics_pipeline_state state;
|
||||||
int num_targets;
|
|
||||||
u64 renderpass_key;
|
u64 renderpass_key;
|
||||||
|
|
||||||
bool operator==(const pipeline_props& other) const
|
bool operator==(const pipeline_props& other) const
|
||||||
{
|
{
|
||||||
if (memcmp(&state.att_state[0], &other.state.att_state[0], sizeof(VkPipelineColorBlendAttachmentState)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (renderpass_key != other.renderpass_key)
|
if (renderpass_key != other.renderpass_key)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (memcmp(&state.rs, &other.state.rs, sizeof(VkPipelineRasterizationStateCreateInfo)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
//Cannot memcmp cs due to pAttachments being a pointer to memory
|
|
||||||
if (state.cs.attachmentCount != other.state.cs.attachmentCount ||
|
|
||||||
state.cs.flags != other.state.cs.flags ||
|
|
||||||
state.cs.logicOp != other.state.cs.logicOp ||
|
|
||||||
state.cs.logicOpEnable != other.state.cs.logicOpEnable ||
|
|
||||||
state.cs.sType != other.state.cs.sType ||
|
|
||||||
memcmp(state.cs.blendConstants, other.state.cs.blendConstants, 4 * sizeof(f32)))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (memcmp(&state.ia, &other.state.ia, sizeof(VkPipelineInputAssemblyStateCreateInfo)))
|
if (memcmp(&state.ia, &other.state.ia, sizeof(VkPipelineInputAssemblyStateCreateInfo)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (memcmp(&state.att_state[0], &other.state.att_state[0], sizeof(VkPipelineColorBlendAttachmentState)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (memcmp(&state.rs, &other.state.rs, sizeof(VkPipelineRasterizationStateCreateInfo)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Cannot memcmp cs due to pAttachments being a pointer to memory
|
||||||
|
if (state.cs.logicOp != other.state.cs.logicOp ||
|
||||||
|
state.cs.logicOpEnable != other.state.cs.logicOpEnable ||
|
||||||
|
memcmp(state.cs.blendConstants, other.state.cs.blendConstants, 4 * sizeof(f32)))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (memcmp(&state.ds, &other.state.ds, sizeof(VkPipelineDepthStencilStateCreateInfo)))
|
if (memcmp(&state.ds, &other.state.ds, sizeof(VkPipelineDepthStencilStateCreateInfo)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return num_targets == other.num_targets;
|
if (state.ms.rasterizationSamples != VK_SAMPLE_COUNT_1_BIT)
|
||||||
|
{
|
||||||
|
if (memcmp(&state.ms, &other.state.ms, sizeof(VkPipelineMultisampleStateCreateInfo)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -50,10 +52,11 @@ namespace rpcs3
|
||||||
template <>
|
template <>
|
||||||
size_t hash_struct<vk::pipeline_props>(const vk::pipeline_props &pipelineProperties)
|
size_t hash_struct<vk::pipeline_props>(const vk::pipeline_props &pipelineProperties)
|
||||||
{
|
{
|
||||||
size_t seed = hash_base(pipelineProperties.num_targets);
|
size_t seed = hash_base(pipelineProperties.renderpass_key);
|
||||||
seed ^= hash_struct(pipelineProperties.state.ia);
|
seed ^= hash_struct(pipelineProperties.state.ia);
|
||||||
seed ^= hash_struct(pipelineProperties.state.ds);
|
seed ^= hash_struct(pipelineProperties.state.ds);
|
||||||
seed ^= hash_struct(pipelineProperties.state.rs);
|
seed ^= hash_struct(pipelineProperties.state.rs);
|
||||||
|
seed ^= hash_struct(pipelineProperties.state.ms);
|
||||||
|
|
||||||
// Do not compare pointers to memory!
|
// Do not compare pointers to memory!
|
||||||
VkPipelineColorBlendStateCreateInfo tmp;
|
VkPipelineColorBlendStateCreateInfo tmp;
|
||||||
|
@ -134,10 +137,13 @@ struct VKTraits
|
||||||
vp.viewportCount = 1;
|
vp.viewportCount = 1;
|
||||||
vp.scissorCount = 1;
|
vp.scissorCount = 1;
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo ms = {};
|
VkPipelineMultisampleStateCreateInfo ms = pipelineProperties.state.ms;
|
||||||
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
verify("Multisample state mismatch!" HERE), ms.rasterizationSamples == VkSampleCountFlagBits((pipelineProperties.renderpass_key >> 16) & 0xF);
|
||||||
ms.pSampleMask = NULL;
|
if (ms.rasterizationSamples != VK_SAMPLE_COUNT_1_BIT)
|
||||||
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
{
|
||||||
|
// Update the sample mask pointer
|
||||||
|
ms.pSampleMask = &pipelineProperties.state.temp_storage.msaa_sample_mask;
|
||||||
|
}
|
||||||
|
|
||||||
// Rebase pointers from pipeline structure in case it is moved/copied
|
// Rebase pointers from pipeline structure in case it is moved/copied
|
||||||
VkPipelineColorBlendStateCreateInfo cs = pipelineProperties.state.cs;
|
VkPipelineColorBlendStateCreateInfo cs = pipelineProperties.state.cs;
|
||||||
|
|
|
@ -9,13 +9,9 @@ namespace vk
|
||||||
{
|
{
|
||||||
using namespace ::glsl;
|
using namespace ::glsl;
|
||||||
|
|
||||||
program::program(VkDevice dev, VkPipeline p, const std::vector<program_input> &vertex_input, const std::vector<program_input>& fragment_inputs)
|
void program::create_impl()
|
||||||
: m_device(dev), pipeline(p)
|
|
||||||
{
|
{
|
||||||
linked = false;
|
linked = false;
|
||||||
|
|
||||||
load_uniforms(program_domain::glsl_vertex_program, vertex_input);
|
|
||||||
load_uniforms(program_domain::glsl_vertex_program, fragment_inputs);
|
|
||||||
attribute_location_mask = 0;
|
attribute_location_mask = 0;
|
||||||
vertex_attributes_mask = 0;
|
vertex_attributes_mask = 0;
|
||||||
|
|
||||||
|
@ -24,12 +20,26 @@ namespace vk
|
||||||
vs_texture_bindings.fill(~0u);
|
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)
|
||||||
|
{
|
||||||
|
create_impl();
|
||||||
|
load_uniforms(vertex_input);
|
||||||
|
load_uniforms(fragment_inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
program::program(VkDevice dev, VkPipeline p)
|
||||||
|
: m_device(dev), pipeline(p)
|
||||||
|
{
|
||||||
|
create_impl();
|
||||||
|
}
|
||||||
|
|
||||||
program::~program()
|
program::~program()
|
||||||
{
|
{
|
||||||
vkDestroyPipeline(m_device, pipeline, nullptr);
|
vkDestroyPipeline(m_device, pipeline, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
program& program::load_uniforms(program_domain domain, const std::vector<program_input>& inputs)
|
program& program::load_uniforms(const std::vector<program_input>& inputs)
|
||||||
{
|
{
|
||||||
verify("Cannot change uniforms in already linked program!" HERE), !linked;
|
verify("Cannot change uniforms in already linked program!" HERE), !linked;
|
||||||
|
|
||||||
|
@ -92,7 +102,7 @@ namespace vk
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void program::bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string& uniform_name, VkDescriptorSet &descriptor_set)
|
void program::bind_uniform(const VkDescriptorImageInfo &image_descriptor, const std::string& uniform_name, VkDescriptorType type, VkDescriptorSet &descriptor_set)
|
||||||
{
|
{
|
||||||
for (const auto &uniform : uniforms[program_input_type::input_type_texture])
|
for (const auto &uniform : uniforms[program_input_type::input_type_texture])
|
||||||
{
|
{
|
||||||
|
@ -106,7 +116,7 @@ namespace vk
|
||||||
uniform.location, // dstBinding
|
uniform.location, // dstBinding
|
||||||
0, // dstArrayElement
|
0, // dstArrayElement
|
||||||
1, // descriptorCount
|
1, // descriptorCount
|
||||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, // descriptorType
|
type, // descriptorType
|
||||||
&image_descriptor, // pImageInfo
|
&image_descriptor, // pImageInfo
|
||||||
nullptr, // pBufferInfo
|
nullptr, // pBufferInfo
|
||||||
nullptr // pTexelBufferView
|
nullptr // pTexelBufferView
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace vk
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode
|
// Decode
|
||||||
VkSampleCountFlagBits samples = VkSampleCountFlagBits((renderpass_key >> 16) & 0x1F);
|
VkSampleCountFlagBits samples = VkSampleCountFlagBits((renderpass_key >> 16) & 0xF);
|
||||||
std::vector<VkImageLayout> rtv_layouts;
|
std::vector<VkImageLayout> rtv_layouts;
|
||||||
VkImageLayout dsv_layout;
|
VkImageLayout dsv_layout;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
|
|
||||||
namespace vk
|
namespace vk
|
||||||
{
|
{
|
||||||
|
void resolve_image(vk::command_buffer& cmd, vk::viewable_image* dst, vk::viewable_image* src);
|
||||||
|
void unresolve_image(vk::command_buffer& cmd, vk::viewable_image* dst, vk::viewable_image* src);
|
||||||
|
|
||||||
struct render_target : public viewable_image, public rsx::ref_counted, public rsx::render_target_descriptor<vk::viewable_image*>
|
struct render_target : public viewable_image, public rsx::ref_counted, public rsx::render_target_descriptor<vk::viewable_image*>
|
||||||
{
|
{
|
||||||
u64 frame_tag = 0; // frame id when invalidated, 0 if not invalid
|
u64 frame_tag = 0; // frame id when invalidated, 0 if not invalid
|
||||||
|
@ -19,8 +22,14 @@ namespace vk
|
||||||
|
|
||||||
vk::viewable_image* get_surface(rsx::surface_access access_type) override
|
vk::viewable_image* get_surface(rsx::surface_access access_type) override
|
||||||
{
|
{
|
||||||
// TODO
|
if (spp == 1 || access_type == rsx::surface_access::write)
|
||||||
return (vk::viewable_image*)this;
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A read barrier should have been called before this!
|
||||||
|
verify("Read access without explicit barrier" HERE), resolve_surface, !(msaa_flags & rsx::surface_state_flags::require_resolve);
|
||||||
|
return resolve_surface.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_depth_surface() const override
|
bool is_depth_surface() const override
|
||||||
|
@ -39,58 +48,282 @@ namespace vk
|
||||||
return (rsx::apply_resolution_scale(_width, true) == width()) && (rsx::apply_resolution_scale(_height, true) == height());
|
return (rsx::apply_resolution_scale(_width, true) == width()) && (rsx::apply_resolution_scale(_height, true) == height());
|
||||||
}
|
}
|
||||||
|
|
||||||
void memory_barrier(vk::command_buffer& cmd, bool force_init = false)
|
image_view* get_view(u32 remap_encoding, const std::pair<std::array<u8, 4>, std::array<u8, 4>>& remap,
|
||||||
|
VkImageAspectFlags mask = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT) override
|
||||||
|
{
|
||||||
|
if (remap_encoding != 0xDEADBEEF && resolve_surface)
|
||||||
|
{
|
||||||
|
return resolve_surface->get_view(remap_encoding, remap, mask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (remap_encoding == 0xDEADBEEF)
|
||||||
|
{
|
||||||
|
// Special encoding to skip the resolve target fetch
|
||||||
|
remap_encoding = 0xAAE4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vk::viewable_image::get_view(remap_encoding, remap, mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolve(vk::command_buffer& cmd)
|
||||||
|
{
|
||||||
|
VkImageSubresourceRange range = { aspect(), 0, 1, 0, 1 };
|
||||||
|
|
||||||
|
// NOTE: This surface can only be in the ATTACHMENT_OPTIMAL layout
|
||||||
|
// The resolve surface can be in any type of access, but we have to assume it is likely in read-only mode like shader read-only
|
||||||
|
|
||||||
|
if (LIKELY(!is_depth_surface()))
|
||||||
|
{
|
||||||
|
verify(HERE), current_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
|
||||||
|
// This is the source; finish writing before reading
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, this->value,
|
||||||
|
this->current_layout, VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
// This is the target; finish reading before writing
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, resolve_surface->value,
|
||||||
|
resolve_surface->current_layout, VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
this->current_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
resolve_surface->current_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->push_layout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
resolve_surface->change_layout(cmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::resolve_image(cmd, resolve_surface.get(), this);
|
||||||
|
|
||||||
|
if (LIKELY(!is_depth_surface()))
|
||||||
|
{
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, this->value,
|
||||||
|
this->current_layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, resolve_surface->value,
|
||||||
|
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
this->current_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
resolve_surface->current_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->pop_layout(cmd);
|
||||||
|
resolve_surface->change_layout(cmd, VK_IMAGE_LAYOUT_GENERAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
msaa_flags &= ~(rsx::surface_state_flags::require_resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unresolve(vk::command_buffer& cmd)
|
||||||
|
{
|
||||||
|
verify(HERE), !(msaa_flags & rsx::surface_state_flags::require_resolve);
|
||||||
|
VkImageSubresourceRange range = { aspect(), 0, 1, 0, 1 };
|
||||||
|
|
||||||
|
if (LIKELY(!is_depth_surface()))
|
||||||
|
{
|
||||||
|
verify(HERE), current_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
|
||||||
|
// This is the dest; finish reading before writing
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, this->value,
|
||||||
|
this->current_layout, VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
// This is the source; finish writing before reading
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, resolve_surface->value,
|
||||||
|
resolve_surface->current_layout, VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
this->current_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
resolve_surface->current_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->push_layout(cmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||||
|
resolve_surface->change_layout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::unresolve_image(cmd, this, resolve_surface.get());
|
||||||
|
|
||||||
|
if (LIKELY(!is_depth_surface()))
|
||||||
|
{
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, this->value,
|
||||||
|
this->current_layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||||
|
VK_ACCESS_SHADER_WRITE_BIT,
|
||||||
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
vk::insert_image_memory_barrier(
|
||||||
|
cmd, resolve_surface->value,
|
||||||
|
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_ACCESS_SHADER_READ_BIT,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
range);
|
||||||
|
|
||||||
|
this->current_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
|
resolve_surface->current_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->pop_layout(cmd);
|
||||||
|
resolve_surface->change_layout(cmd, VK_IMAGE_LAYOUT_GENERAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
msaa_flags &= ~(rsx::surface_state_flags::require_unresolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_barrier(vk::command_buffer& cmd, rsx::surface_access access)
|
||||||
{
|
{
|
||||||
// Helper to optionally clear/initialize memory contents depending on barrier type
|
// Helper to optionally clear/initialize memory contents depending on barrier type
|
||||||
auto clear_surface_impl = [&]()
|
auto clear_surface_impl = [&cmd, this](vk::image* surface)
|
||||||
{
|
{
|
||||||
push_layout(cmd, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
const auto optimal_layout = (surface->current_layout == VK_IMAGE_LAYOUT_GENERAL) ?
|
||||||
VkImageSubresourceRange range{ aspect(), 0, 1, 0, 1 };
|
VK_IMAGE_LAYOUT_GENERAL :
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
|
||||||
if (aspect() & VK_IMAGE_ASPECT_COLOR_BIT)
|
surface->push_layout(cmd, optimal_layout);
|
||||||
|
|
||||||
|
VkImageSubresourceRange range{ surface->aspect(), 0, 1, 0, 1 };
|
||||||
|
if (surface->aspect() & VK_IMAGE_ASPECT_COLOR_BIT)
|
||||||
{
|
{
|
||||||
VkClearColorValue color{};
|
VkClearColorValue color{};
|
||||||
vkCmdClearColorImage(cmd, value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range);
|
vkCmdClearColorImage(cmd, surface->value, surface->current_layout, &color, 1, &range);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
VkClearDepthStencilValue clear{ 1.f, 255 };
|
VkClearDepthStencilValue clear{ 1.f, 255 };
|
||||||
vkCmdClearDepthStencilImage(cmd, value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear, 1, &range);
|
vkCmdClearDepthStencilImage(cmd, surface->value, surface->current_layout, &clear, 1, &range);
|
||||||
}
|
}
|
||||||
|
|
||||||
pop_layout(cmd);
|
surface->pop_layout(cmd);
|
||||||
|
|
||||||
|
if (surface == this)
|
||||||
|
{
|
||||||
state_flags &= ~rsx::surface_state_flags::erase_bkgnd;
|
state_flags &= ~rsx::surface_state_flags::erase_bkgnd;
|
||||||
};
|
|
||||||
|
|
||||||
auto null_transfer_impl = [&]()
|
|
||||||
{
|
|
||||||
if (dirty() && (force_init || state_flags & rsx::surface_state_flags::erase_bkgnd))
|
|
||||||
{
|
|
||||||
// Initialize memory contents if we did not find anything usable
|
|
||||||
// TODO: Properly sync with Cell
|
|
||||||
clear_surface_impl();
|
|
||||||
on_write();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
verify(HERE), state_flags == rsx::surface_state_flags::ready;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!old_contents)
|
auto get_resolve_target = [&]()
|
||||||
{
|
{
|
||||||
null_transfer_impl();
|
if (!resolve_surface)
|
||||||
|
{
|
||||||
|
// Create a resolve surface
|
||||||
|
auto pdev = vk::get_current_renderer();
|
||||||
|
const auto resolve_w = width() * samples_x;
|
||||||
|
const auto resolve_h = height() * samples_y;
|
||||||
|
|
||||||
|
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
usage |= (this->info.usage & (VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT));
|
||||||
|
|
||||||
|
resolve_surface.reset(new vk::viewable_image(
|
||||||
|
*pdev,
|
||||||
|
pdev->get_memory_mapping().device_local,
|
||||||
|
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||||
|
VK_IMAGE_TYPE_2D,
|
||||||
|
format(),
|
||||||
|
resolve_w, resolve_h, 1, 1, 1,
|
||||||
|
VK_SAMPLE_COUNT_1_BIT,
|
||||||
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
|
VK_IMAGE_TILING_OPTIMAL,
|
||||||
|
usage,
|
||||||
|
0));
|
||||||
|
|
||||||
|
resolve_surface->native_component_map = native_component_map;
|
||||||
|
resolve_surface->change_layout(cmd, VK_IMAGE_LAYOUT_GENERAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve_surface.get();
|
||||||
|
};
|
||||||
|
|
||||||
|
const bool read_access = (access != rsx::surface_access::write);
|
||||||
|
if (spp > 1 && read_access)
|
||||||
|
{
|
||||||
|
get_resolve_target();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old_contents && !rsx::pitch_compatible(this, static_cast<vk::render_target*>(old_contents.source)))
|
||||||
|
{
|
||||||
|
LOG_TRACE(RSX, "Pitch mismatch, could not transfer inherited memory");
|
||||||
|
clear_rw_barrier();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LIKELY(!old_contents))
|
||||||
|
{
|
||||||
|
if (state_flags & rsx::surface_state_flags::erase_bkgnd)
|
||||||
|
{
|
||||||
|
clear_surface_impl(this);
|
||||||
|
|
||||||
|
if (resolve_surface && read_access)
|
||||||
|
{
|
||||||
|
// Only clear the resolve surface if reading from it, otherwise it's a waste
|
||||||
|
clear_surface_impl(resolve_surface.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
on_write(rsx::get_shared_tag(), rsx::surface_state_flags::ready);
|
||||||
|
}
|
||||||
|
else if (msaa_flags & rsx::surface_state_flags::require_resolve)
|
||||||
|
{
|
||||||
|
if (read_access)
|
||||||
|
{
|
||||||
|
// Only do this step when read access is required
|
||||||
|
resolve(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (msaa_flags & rsx::surface_state_flags::require_unresolve)
|
||||||
|
{
|
||||||
|
if (!read_access)
|
||||||
|
{
|
||||||
|
// Only do this step when it is needed to start rendering
|
||||||
|
unresolve(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto src_texture = static_cast<vk::render_target*>(old_contents.source);
|
auto src_texture = static_cast<vk::render_target*>(old_contents.source);
|
||||||
if (!rsx::pitch_compatible(this, src_texture))
|
src_texture->read_barrier(cmd);
|
||||||
{
|
|
||||||
LOG_TRACE(RSX, "Pitch mismatch, could not transfer inherited memory");
|
|
||||||
|
|
||||||
clear_rw_barrier();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto src_bpp = src_texture->get_bpp();
|
const auto src_bpp = src_texture->get_bpp();
|
||||||
const auto dst_bpp = get_bpp();
|
const auto dst_bpp = get_bpp();
|
||||||
|
@ -116,29 +349,52 @@ namespace vk
|
||||||
vk::blitter hw_blitter;
|
vk::blitter hw_blitter;
|
||||||
old_contents.init_transfer(this);
|
old_contents.init_transfer(this);
|
||||||
|
|
||||||
if (state_flags & rsx::surface_state_flags::erase_bkgnd)
|
auto src_area = old_contents.src_rect();
|
||||||
|
auto dst_area = old_contents.dst_rect();
|
||||||
|
|
||||||
|
src_texture->transform_pixels_to_samples(src_area);
|
||||||
|
this->transform_pixels_to_samples(dst_area);
|
||||||
|
|
||||||
|
vk::image *target_image = (spp > 1) ? get_resolve_target() : this;
|
||||||
|
if (dst_area.x1 == 0 && dst_area.y1 == 0 &&
|
||||||
|
unsigned(dst_area.x2) == target_image->width() && unsigned(dst_area.y2) == target_image->height())
|
||||||
{
|
{
|
||||||
const auto area = old_contents.dst_rect();
|
// Skip a bunch of useless work
|
||||||
if (area.x1 > 0 || area.y1 > 0 || unsigned(area.x2) < width() || unsigned(area.y2) < height())
|
state_flags &= ~(rsx::surface_state_flags::erase_bkgnd);
|
||||||
{
|
msaa_flags = rsx::surface_state_flags::ready;
|
||||||
clear_surface_impl();
|
|
||||||
}
|
}
|
||||||
else
|
else if (state_flags & rsx::surface_state_flags::erase_bkgnd)
|
||||||
{
|
{
|
||||||
state_flags &= ~rsx::surface_state_flags::erase_bkgnd;
|
clear_surface_impl(target_image);
|
||||||
|
|
||||||
|
state_flags &= ~(rsx::surface_state_flags::erase_bkgnd);
|
||||||
|
msaa_flags = rsx::surface_state_flags::ready;
|
||||||
}
|
}
|
||||||
|
else if (msaa_flags & rsx::surface_state_flags::require_resolve)
|
||||||
|
{
|
||||||
|
// Need to forward resolve this
|
||||||
|
resolve(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
hw_blitter.scale_image(cmd, old_contents.source, this,
|
hw_blitter.scale_image(
|
||||||
old_contents.src_rect(),
|
cmd,
|
||||||
old_contents.dst_rect(),
|
src_texture->get_surface(rsx::surface_access::read),
|
||||||
|
this->get_surface(rsx::surface_access::transfer),
|
||||||
|
src_area,
|
||||||
|
dst_area,
|
||||||
/*linear?*/false, /*depth?(unused)*/false, typeless_info);
|
/*linear?*/false, /*depth?(unused)*/false, typeless_info);
|
||||||
|
|
||||||
on_write();
|
on_write_copy();
|
||||||
|
|
||||||
|
if (!read_access && spp > 1)
|
||||||
|
{
|
||||||
|
// Write barrier, must initialize
|
||||||
|
unresolve(cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_barrier(vk::command_buffer& cmd) { memory_barrier(cmd, true); }
|
void read_barrier(vk::command_buffer& cmd) { memory_barrier(cmd, rsx::surface_access::read); }
|
||||||
void write_barrier(vk::command_buffer& cmd) { memory_barrier(cmd, false); }
|
void write_barrier(vk::command_buffer& cmd) { memory_barrier(cmd, rsx::surface_access::write); }
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline vk::render_target* as_rtt(vk::image* t)
|
static inline vk::render_target* as_rtt(vk::image* t)
|
||||||
|
@ -164,8 +420,19 @@ namespace rsx
|
||||||
rsx::surface_antialiasing antialias,
|
rsx::surface_antialiasing antialias,
|
||||||
vk::render_device &device, vk::command_buffer& cmd)
|
vk::render_device &device, vk::command_buffer& cmd)
|
||||||
{
|
{
|
||||||
auto fmt = vk::get_compatible_surface_format(format);
|
const auto fmt = vk::get_compatible_surface_format(format);
|
||||||
|
const auto spp = get_format_sample_count(antialias);
|
||||||
VkFormat requested_format = fmt.first;
|
VkFormat requested_format = fmt.first;
|
||||||
|
VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
|
|
||||||
|
if (antialias == rsx::surface_antialiasing::center_1_sample)
|
||||||
|
{
|
||||||
|
usage_flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
usage_flags |= VK_IMAGE_USAGE_STORAGE_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<vk::render_target> rtt;
|
std::unique_ptr<vk::render_target> rtt;
|
||||||
rtt = std::make_unique<vk::render_target>(device, device.get_memory_mapping().device_local,
|
rtt = std::make_unique<vk::render_target>(device, device.get_memory_mapping().device_local,
|
||||||
|
@ -173,13 +440,13 @@ namespace rsx
|
||||||
VK_IMAGE_TYPE_2D,
|
VK_IMAGE_TYPE_2D,
|
||||||
requested_format,
|
requested_format,
|
||||||
static_cast<uint32_t>(rsx::apply_resolution_scale((u16)width, true)), static_cast<uint32_t>(rsx::apply_resolution_scale((u16)height, true)), 1, 1, 1,
|
static_cast<uint32_t>(rsx::apply_resolution_scale((u16)width, true)), static_cast<uint32_t>(rsx::apply_resolution_scale((u16)height, true)), 1, 1, 1,
|
||||||
VK_SAMPLE_COUNT_1_BIT,
|
static_cast<VkSampleCountFlagBits>(spp),
|
||||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
VK_IMAGE_TILING_OPTIMAL,
|
VK_IMAGE_TILING_OPTIMAL,
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_SAMPLED_BIT,
|
usage_flags,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
change_image_layout(cmd, rtt.get(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_COLOR_BIT));
|
rtt->change_layout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
rtt->set_format(format);
|
rtt->set_format(format);
|
||||||
rtt->set_aa_mode(antialias);
|
rtt->set_aa_mode(antialias);
|
||||||
|
@ -187,7 +454,7 @@ namespace rsx
|
||||||
rtt->state_flags = rsx::surface_state_flags::erase_bkgnd;
|
rtt->state_flags = rsx::surface_state_flags::erase_bkgnd;
|
||||||
rtt->native_component_map = fmt.second;
|
rtt->native_component_map = fmt.second;
|
||||||
rtt->rsx_pitch = (u16)pitch;
|
rtt->rsx_pitch = (u16)pitch;
|
||||||
rtt->native_pitch = (u16)width * get_format_block_size_in_bytes(format) * get_format_sample_count(antialias);
|
rtt->native_pitch = (u16)width * get_format_block_size_in_bytes(format) * rtt->samples_x;
|
||||||
rtt->surface_width = (u16)width;
|
rtt->surface_width = (u16)width;
|
||||||
rtt->surface_height = (u16)height;
|
rtt->surface_height = (u16)height;
|
||||||
rtt->queue_tag(address);
|
rtt->queue_tag(address);
|
||||||
|
@ -203,13 +470,14 @@ namespace rsx
|
||||||
rsx::surface_antialiasing antialias,
|
rsx::surface_antialiasing antialias,
|
||||||
vk::render_device &device, vk::command_buffer& cmd)
|
vk::render_device &device, vk::command_buffer& cmd)
|
||||||
{
|
{
|
||||||
VkFormat requested_format = vk::get_compatible_depth_surface_format(device.get_formats_support(), format);
|
const VkFormat requested_format = vk::get_compatible_depth_surface_format(device.get_formats_support(), format);
|
||||||
VkImageSubresourceRange range = vk::get_image_subresource_range(0, 0, 1, 1, VK_IMAGE_ASPECT_DEPTH_BIT);
|
const auto spp = get_format_sample_count(antialias);
|
||||||
|
VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||||
|
|
||||||
if (requested_format != VK_FORMAT_D16_UNORM)
|
if (antialias == rsx::surface_antialiasing::center_1_sample)
|
||||||
range.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
|
{
|
||||||
|
usage_flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
const auto scale = rsx::get_resolution_scale();
|
}
|
||||||
|
|
||||||
std::unique_ptr<vk::render_target> ds;
|
std::unique_ptr<vk::render_target> ds;
|
||||||
ds = std::make_unique<vk::render_target>(device, device.get_memory_mapping().device_local,
|
ds = std::make_unique<vk::render_target>(device, device.get_memory_mapping().device_local,
|
||||||
|
@ -217,12 +485,13 @@ namespace rsx
|
||||||
VK_IMAGE_TYPE_2D,
|
VK_IMAGE_TYPE_2D,
|
||||||
requested_format,
|
requested_format,
|
||||||
static_cast<uint32_t>(rsx::apply_resolution_scale((u16)width, true)), static_cast<uint32_t>(rsx::apply_resolution_scale((u16)height, true)), 1, 1, 1,
|
static_cast<uint32_t>(rsx::apply_resolution_scale((u16)width, true)), static_cast<uint32_t>(rsx::apply_resolution_scale((u16)height, true)), 1, 1, 1,
|
||||||
VK_SAMPLE_COUNT_1_BIT,
|
static_cast<VkSampleCountFlagBits>(spp),
|
||||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
VK_IMAGE_TILING_OPTIMAL,
|
VK_IMAGE_TILING_OPTIMAL,
|
||||||
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT| VK_IMAGE_USAGE_TRANSFER_SRC_BIT| VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_SAMPLED_BIT,
|
usage_flags,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
|
ds->change_layout(cmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
ds->set_format(format);
|
ds->set_format(format);
|
||||||
ds->set_aa_mode(antialias);
|
ds->set_aa_mode(antialias);
|
||||||
|
@ -230,9 +499,7 @@ namespace rsx
|
||||||
ds->state_flags = rsx::surface_state_flags::erase_bkgnd;
|
ds->state_flags = rsx::surface_state_flags::erase_bkgnd;
|
||||||
ds->native_component_map = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R };
|
ds->native_component_map = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R };
|
||||||
|
|
||||||
change_image_layout(cmd, ds.get(), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, range);
|
ds->native_pitch = (u16)width * 2 * ds->samples_x;
|
||||||
|
|
||||||
ds->native_pitch = (u16)width * 2 * get_format_sample_count(antialias);
|
|
||||||
if (format == rsx::surface_depth_format::z24s8)
|
if (format == rsx::surface_depth_format::z24s8)
|
||||||
ds->native_pitch *= 2;
|
ds->native_pitch *= 2;
|
||||||
|
|
||||||
|
@ -261,7 +528,7 @@ namespace rsx
|
||||||
VK_IMAGE_TYPE_2D,
|
VK_IMAGE_TYPE_2D,
|
||||||
ref->format(),
|
ref->format(),
|
||||||
new_w, new_h, 1, 1, 1,
|
new_w, new_h, 1, 1, 1,
|
||||||
(VkSampleCountFlagBits)ref->samples(),
|
static_cast<VkSampleCountFlagBits>(ref->samples()),
|
||||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||||
VK_IMAGE_TILING_OPTIMAL,
|
VK_IMAGE_TILING_OPTIMAL,
|
||||||
ref->info.usage,
|
ref->info.usage,
|
||||||
|
@ -273,12 +540,16 @@ namespace rsx
|
||||||
sink->memory_usage_flags = rsx::surface_usage_flags::storage;
|
sink->memory_usage_flags = rsx::surface_usage_flags::storage;
|
||||||
sink->state_flags = rsx::surface_state_flags::erase_bkgnd;
|
sink->state_flags = rsx::surface_state_flags::erase_bkgnd;
|
||||||
sink->native_component_map = ref->native_component_map;
|
sink->native_component_map = ref->native_component_map;
|
||||||
sink->native_pitch = u16(prev.width * ref->get_bpp() * ref->get_spp());
|
sink->native_pitch = u16(prev.width * ref->get_bpp() * ref->samples_x);
|
||||||
sink->surface_width = prev.width;
|
sink->surface_width = prev.width;
|
||||||
sink->surface_height = prev.height;
|
sink->surface_height = prev.height;
|
||||||
sink->queue_tag(address);
|
sink->queue_tag(address);
|
||||||
|
|
||||||
change_image_layout(cmd, sink.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
const auto best_layout = (ref->info.usage & VK_IMAGE_USAGE_SAMPLED_BIT) ?
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL :
|
||||||
|
ref->current_layout;
|
||||||
|
|
||||||
|
sink->change_layout(cmd, best_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
prev.target = sink.get();
|
prev.target = sink.get();
|
||||||
|
@ -313,9 +584,12 @@ namespace rsx
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepare_surface_for_sampling(vk::command_buffer& cmd, vk::render_target *surface)
|
static void prepare_surface_for_sampling(vk::command_buffer& cmd, vk::render_target *surface)
|
||||||
|
{
|
||||||
|
if (surface->info.usage & VK_IMAGE_USAGE_SAMPLED_BIT)
|
||||||
{
|
{
|
||||||
surface->change_layout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
surface->change_layout(cmd, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool surface_is_pitch_compatible(const std::unique_ptr<vk::render_target> &surface, size_t pitch)
|
static bool surface_is_pitch_compatible(const std::unique_ptr<vk::render_target> &surface, size_t pitch)
|
||||||
{
|
{
|
||||||
|
@ -325,7 +599,6 @@ namespace rsx
|
||||||
static void invalidate_surface_contents(vk::command_buffer& /*cmd*/, vk::render_target *surface, u32 address, size_t pitch)
|
static void invalidate_surface_contents(vk::command_buffer& /*cmd*/, vk::render_target *surface, u32 address, size_t pitch)
|
||||||
{
|
{
|
||||||
surface->rsx_pitch = (u16)pitch;
|
surface->rsx_pitch = (u16)pitch;
|
||||||
surface->set_aa_mode(rsx::surface_antialiasing::center_1_sample);
|
|
||||||
surface->queue_tag(address);
|
surface->queue_tag(address);
|
||||||
surface->last_use_tag = 0;
|
surface->last_use_tag = 0;
|
||||||
surface->memory_usage_flags = rsx::surface_usage_flags::unknown;
|
surface->memory_usage_flags = rsx::surface_usage_flags::unknown;
|
||||||
|
@ -345,7 +618,7 @@ namespace rsx
|
||||||
surface->release();
|
surface->release();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void notify_surface_persist(const std::unique_ptr<vk::render_target> &surface)
|
static void notify_surface_persist(const std::unique_ptr<vk::render_target>& /*surface*/)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
static void notify_surface_reused(const std::unique_ptr<vk::render_target> &surface)
|
static void notify_surface_reused(const std::unique_ptr<vk::render_target> &surface)
|
||||||
|
|
181
rpcs3/Emu/RSX/VK/VKResolveHelper.cpp
Normal file
181
rpcs3/Emu/RSX/VK/VKResolveHelper.cpp
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
#include "VKResolveHelper.h"
|
||||||
|
#include "VKRenderPass.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const char *get_format_prefix(VkFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
||||||
|
return "r16ui";
|
||||||
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||||
|
return "rgba8";
|
||||||
|
case VK_FORMAT_R16G16B16A16_SFLOAT:
|
||||||
|
return "rgba16f";
|
||||||
|
case VK_FORMAT_R32G32B32A32_SFLOAT:
|
||||||
|
return "rgba32f";
|
||||||
|
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
|
||||||
|
return "r16ui";
|
||||||
|
case VK_FORMAT_R8_UNORM:
|
||||||
|
return "r8";
|
||||||
|
case VK_FORMAT_R8G8_UNORM:
|
||||||
|
return "rg8";
|
||||||
|
case VK_FORMAT_R32_SFLOAT:
|
||||||
|
return "r32f";
|
||||||
|
default:
|
||||||
|
fmt::throw_exception("Unhandled VkFormat 0x%x" HERE, u32(format));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace vk
|
||||||
|
{
|
||||||
|
std::unordered_map<VkFormat, std::unique_ptr<vk::cs_resolve_task>> g_resolve_helpers;
|
||||||
|
std::unordered_map<VkFormat, std::unique_ptr<vk::cs_unresolve_task>> g_unresolve_helpers;
|
||||||
|
std::unique_ptr<vk::depthonly_resolve> g_depth_resolver;
|
||||||
|
std::unique_ptr<vk::depthonly_unresolve> g_depth_unresolver;
|
||||||
|
std::unique_ptr<vk::depthstencil_resolve_AMD> g_depthstencil_resolverAMD;
|
||||||
|
std::unique_ptr<vk::depthstencil_unresolve_AMD> g_depthstencil_unresolverAMD;
|
||||||
|
|
||||||
|
void resolve_image(vk::command_buffer& cmd, vk::viewable_image* dst, vk::viewable_image* src)
|
||||||
|
{
|
||||||
|
if (src->aspect() == VK_IMAGE_ASPECT_COLOR_BIT)
|
||||||
|
{
|
||||||
|
auto &job = g_resolve_helpers[src->format()];
|
||||||
|
|
||||||
|
if (!job)
|
||||||
|
{
|
||||||
|
job.reset(new vk::cs_resolve_task(get_format_prefix(src->format())));
|
||||||
|
}
|
||||||
|
|
||||||
|
job->run(cmd, src, dst);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<vk::image*> surface = { dst };
|
||||||
|
auto& dev = cmd.get_command_pool().get_owner();
|
||||||
|
|
||||||
|
const auto key = vk::get_renderpass_key(surface);
|
||||||
|
auto renderpass = vk::get_renderpass(dev, key);
|
||||||
|
|
||||||
|
if (src->aspect() & VK_IMAGE_ASPECT_STENCIL_BIT)
|
||||||
|
{
|
||||||
|
if (!g_depthstencil_resolverAMD)
|
||||||
|
{
|
||||||
|
g_depthstencil_resolverAMD.reset(new vk::depthstencil_resolve_AMD());
|
||||||
|
g_depthstencil_resolverAMD->create(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_depthstencil_resolverAMD->run(cmd, src, dst, renderpass);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!g_depth_resolver)
|
||||||
|
{
|
||||||
|
g_depth_resolver.reset(new vk::depthonly_resolve());
|
||||||
|
g_depth_resolver->create(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_depth_resolver->run(cmd, src, dst, renderpass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unresolve_image(vk::command_buffer& cmd, vk::viewable_image* dst, vk::viewable_image* src)
|
||||||
|
{
|
||||||
|
if (src->aspect() == VK_IMAGE_ASPECT_COLOR_BIT)
|
||||||
|
{
|
||||||
|
auto &job = g_unresolve_helpers[src->format()];
|
||||||
|
|
||||||
|
if (!job)
|
||||||
|
{
|
||||||
|
job.reset(new vk::cs_unresolve_task(get_format_prefix(src->format())));
|
||||||
|
}
|
||||||
|
|
||||||
|
job->run(cmd, dst, src);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::vector<vk::image*> surface = { dst };
|
||||||
|
auto& dev = cmd.get_command_pool().get_owner();
|
||||||
|
|
||||||
|
const auto key = vk::get_renderpass_key(surface);
|
||||||
|
auto renderpass = vk::get_renderpass(dev, key);
|
||||||
|
|
||||||
|
if (src->aspect() & VK_IMAGE_ASPECT_STENCIL_BIT)
|
||||||
|
{
|
||||||
|
if (!g_depthstencil_unresolverAMD)
|
||||||
|
{
|
||||||
|
g_depthstencil_unresolverAMD.reset(new vk::depthstencil_unresolve_AMD());
|
||||||
|
g_depthstencil_unresolverAMD->create(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_depthstencil_unresolverAMD->run(cmd, dst, src, renderpass);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!g_depth_unresolver)
|
||||||
|
{
|
||||||
|
g_depth_unresolver.reset(new vk::depthonly_unresolve());
|
||||||
|
g_depth_unresolver->create(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_depth_unresolver->run(cmd, dst, src, renderpass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_resolve_helpers()
|
||||||
|
{
|
||||||
|
for (auto &task : g_resolve_helpers)
|
||||||
|
{
|
||||||
|
task.second->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &task : g_unresolve_helpers)
|
||||||
|
{
|
||||||
|
task.second->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
g_resolve_helpers.clear();
|
||||||
|
g_unresolve_helpers.clear();
|
||||||
|
|
||||||
|
if (g_depth_resolver)
|
||||||
|
{
|
||||||
|
g_depth_resolver->destroy();
|
||||||
|
g_depth_resolver.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_depthstencil_resolverAMD)
|
||||||
|
{
|
||||||
|
g_depthstencil_resolverAMD->destroy();
|
||||||
|
g_depthstencil_resolverAMD.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_depth_unresolver)
|
||||||
|
{
|
||||||
|
g_depth_unresolver->destroy();
|
||||||
|
g_depth_unresolver.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_depthstencil_unresolverAMD)
|
||||||
|
{
|
||||||
|
g_depthstencil_unresolverAMD->destroy();
|
||||||
|
g_depthstencil_unresolverAMD.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_resolve_resources()
|
||||||
|
{
|
||||||
|
for (auto &e : g_resolve_helpers) e.second->free_resources();
|
||||||
|
for (auto &e : g_unresolve_helpers) e.second->free_resources();
|
||||||
|
|
||||||
|
if (g_depth_resolver) g_depth_resolver->free_resources();
|
||||||
|
if (g_depth_unresolver) g_depth_unresolver->free_resources();
|
||||||
|
if (g_depthstencil_resolverAMD) g_depthstencil_resolverAMD->free_resources();
|
||||||
|
if (g_depthstencil_unresolverAMD) g_depthstencil_unresolverAMD->free_resources();
|
||||||
|
}
|
||||||
|
}
|
406
rpcs3/Emu/RSX/VK/VKResolveHelper.h
Normal file
406
rpcs3/Emu/RSX/VK/VKResolveHelper.h
Normal file
|
@ -0,0 +1,406 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "VKHelpers.h"
|
||||||
|
#include "VKCompute.h"
|
||||||
|
#include "VKOverlays.h"
|
||||||
|
|
||||||
|
namespace vk
|
||||||
|
{
|
||||||
|
struct cs_resolve_base : compute_task
|
||||||
|
{
|
||||||
|
vk::viewable_image* multisampled;
|
||||||
|
vk::viewable_image* resolve;
|
||||||
|
|
||||||
|
u32 cs_wave_x = 1;
|
||||||
|
u32 cs_wave_y = 1;
|
||||||
|
|
||||||
|
cs_resolve_base()
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~cs_resolve_base()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void build(const std::string& kernel, const std::string& format_prefix, int direction)
|
||||||
|
{
|
||||||
|
create();
|
||||||
|
|
||||||
|
// TODO: Tweak occupancy
|
||||||
|
switch (optimal_group_size)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 64:
|
||||||
|
cs_wave_x = 8;
|
||||||
|
cs_wave_y = 8;
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
cs_wave_x = 8;
|
||||||
|
cs_wave_y = 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::pair<std::string, std::string> syntax_replace[] =
|
||||||
|
{
|
||||||
|
{ "%wx", std::to_string(cs_wave_x) },
|
||||||
|
{ "%wy", std::to_string(cs_wave_y) },
|
||||||
|
};
|
||||||
|
|
||||||
|
m_src =
|
||||||
|
"#version 430\n"
|
||||||
|
"layout(local_size_x=%wx, local_size_y=%wy, local_size_z=1) in;\n"
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
m_src = fmt::replace_all(m_src, syntax_replace);
|
||||||
|
|
||||||
|
if (direction == 0)
|
||||||
|
{
|
||||||
|
m_src +=
|
||||||
|
"layout(set=0, binding=0, " + format_prefix + ") uniform readonly restrict image2DMS multisampled;\n"
|
||||||
|
"layout(set=0, binding=1) uniform writeonly restrict image2D resolve;\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_src +=
|
||||||
|
"layout(set=0, binding=0) uniform writeonly restrict image2DMS multisampled;\n"
|
||||||
|
"layout(set=0, binding=1, " + format_prefix + ") uniform readonly restrict image2D resolve;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_src +=
|
||||||
|
"\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" ivec2 resolve_size = imageSize(resolve);\n"
|
||||||
|
" ivec2 aa_size = imageSize(multisampled);\n"
|
||||||
|
" ivec2 sample_count = resolve_size / aa_size;\n"
|
||||||
|
"\n"
|
||||||
|
" if (any(greaterThanEqual(gl_GlobalInvocationID.xy, uvec2(resolve_size)))) return;"
|
||||||
|
"\n"
|
||||||
|
" ivec2 resolve_coords = ivec2(gl_GlobalInvocationID.xy);\n"
|
||||||
|
" ivec2 aa_coords = resolve_coords / sample_count;\n"
|
||||||
|
" ivec2 sample_loc = ivec2(resolve_coords % sample_count);\n"
|
||||||
|
" int sample_index = sample_loc.x + (sample_loc.y * sample_count.y);\n"
|
||||||
|
+ kernel +
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
LOG_ERROR(RSX, "Compute shader:\n%s", m_src);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<VkDescriptorType, u8>> get_descriptor_layout() override
|
||||||
|
{
|
||||||
|
return
|
||||||
|
{
|
||||||
|
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 2 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void declare_inputs() override
|
||||||
|
{
|
||||||
|
std::vector<vk::glsl::program_input> inputs =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
::glsl::program_domain::glsl_compute_program,
|
||||||
|
vk::glsl::program_input_type::input_type_texture,
|
||||||
|
{}, {},
|
||||||
|
0,
|
||||||
|
"multisampled"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
::glsl::program_domain::glsl_compute_program,
|
||||||
|
vk::glsl::program_input_type::input_type_texture,
|
||||||
|
{}, {},
|
||||||
|
1,
|
||||||
|
"resolve"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
m_program->load_uniforms(inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bind_resources() override
|
||||||
|
{
|
||||||
|
auto msaa_view = multisampled->get_view(0xDEADBEEF, rsx::default_remap_vector);
|
||||||
|
auto resolved_view = resolve->get_view(0xAAE4, rsx::default_remap_vector);
|
||||||
|
m_program->bind_uniform({ VK_NULL_HANDLE, msaa_view->value, multisampled->current_layout }, "multisampled", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_descriptor_set);
|
||||||
|
m_program->bind_uniform({ VK_NULL_HANDLE, resolved_view->value, resolve->current_layout }, "resolve", VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, m_descriptor_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(VkCommandBuffer cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image)
|
||||||
|
{
|
||||||
|
verify(HERE), msaa_image->samples() > 1, resolve_image->samples() == 1;
|
||||||
|
|
||||||
|
multisampled = msaa_image;
|
||||||
|
resolve = resolve_image;
|
||||||
|
|
||||||
|
const u32 invocations_x = align(resolve_image->width(), cs_wave_x) / cs_wave_x;
|
||||||
|
const u32 invocations_y = align(resolve_image->height(), cs_wave_y) / cs_wave_y;
|
||||||
|
|
||||||
|
compute_task::run(cmd, invocations_x, invocations_y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cs_resolve_task : cs_resolve_base
|
||||||
|
{
|
||||||
|
cs_resolve_task(const std::string& format_prefix)
|
||||||
|
{
|
||||||
|
std::string kernel =
|
||||||
|
" vec4 aa_sample = imageLoad(multisampled, aa_coords, sample_index);\n"
|
||||||
|
" imageStore(resolve, resolve_coords, aa_sample);\n";
|
||||||
|
|
||||||
|
build(kernel, format_prefix, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cs_unresolve_task : cs_resolve_base
|
||||||
|
{
|
||||||
|
cs_unresolve_task(const std::string& format_prefix)
|
||||||
|
{
|
||||||
|
std::string kernel =
|
||||||
|
" vec4 resolved_sample = imageLoad(resolve, resolve_coords);\n"
|
||||||
|
" imageStore(multisampled, aa_coords, sample_index, resolved_sample);\n";
|
||||||
|
|
||||||
|
build(kernel, format_prefix, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct depth_resolve_base : public overlay_pass
|
||||||
|
{
|
||||||
|
u8 samples_x = 1;
|
||||||
|
u8 samples_y = 1;
|
||||||
|
|
||||||
|
depth_resolve_base()
|
||||||
|
{
|
||||||
|
renderpass_config.set_depth_mask(true);
|
||||||
|
renderpass_config.enable_depth_test(VK_COMPARE_OP_ALWAYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void build(const std::string& kernel, const std::string& extensions, bool stencil_texturing, bool input_is_multisampled)
|
||||||
|
{
|
||||||
|
vs_src =
|
||||||
|
"#version 450\n"
|
||||||
|
"#extension GL_ARB_separate_shader_objects : enable\n\n"
|
||||||
|
"\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n"
|
||||||
|
" vec2 positions[] = {vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(1., 1.)};\n"
|
||||||
|
" gl_Position = vec4(positions[gl_VertexIndex % 4], 0., 1.);\n"
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
fs_src =
|
||||||
|
"#version 420\n"
|
||||||
|
"#extension GL_ARB_separate_shader_objects : enable\n";
|
||||||
|
fs_src += extensions +
|
||||||
|
"\n"
|
||||||
|
"layout(std140, set=0, binding=0) uniform static_data{ ivec4 regs[8]; };\n"
|
||||||
|
"layout(set=0, binding=1) uniform sampler2D fs0;\n";
|
||||||
|
|
||||||
|
if (stencil_texturing)
|
||||||
|
{
|
||||||
|
m_num_usable_samplers = 2;
|
||||||
|
|
||||||
|
fs_src +=
|
||||||
|
"layout(set=0, binding=2) uniform usampler2D fs1;\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_src +=
|
||||||
|
"layout(pixel_center_integer) in vec4 gl_FragCoord;\n"
|
||||||
|
"\n"
|
||||||
|
"void main()\n"
|
||||||
|
"{\n";
|
||||||
|
fs_src += kernel +
|
||||||
|
"}\n";
|
||||||
|
|
||||||
|
if (input_is_multisampled)
|
||||||
|
{
|
||||||
|
auto sampler_loc = fs_src.find("sampler2D fs0");
|
||||||
|
fs_src.insert(sampler_loc + 9, "MS");
|
||||||
|
|
||||||
|
if (stencil_texturing)
|
||||||
|
{
|
||||||
|
sampler_loc = fs_src.find("sampler2D fs1");
|
||||||
|
fs_src.insert(sampler_loc + 9, "MS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_ERROR(RSX, "Resolve shader:\n%s", fs_src);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_uniforms(vk::glsl::program* /*program*/) override
|
||||||
|
{
|
||||||
|
m_ubo_offset = (u32)m_ubo.alloc<256>(8);
|
||||||
|
auto dst = (s32*)m_ubo.map(m_ubo_offset, 128);
|
||||||
|
dst[0] = samples_x;
|
||||||
|
dst[1] = samples_y;
|
||||||
|
m_ubo.unmap();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_sample_configuration(vk::image* msaa_image)
|
||||||
|
{
|
||||||
|
switch (msaa_image->samples())
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
fmt::throw_exception("MSAA input not multisampled!" HERE);
|
||||||
|
case 2:
|
||||||
|
samples_x = 2;
|
||||||
|
samples_y = 1;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
samples_x = samples_y = 2;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fmt::throw_exception("Unsupported sample count %d" HERE, msaa_image->samples());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct depthonly_resolve : depth_resolve_base
|
||||||
|
{
|
||||||
|
depthonly_resolve()
|
||||||
|
{
|
||||||
|
build(
|
||||||
|
" ivec2 out_coord = ivec2(gl_FragCoord.xy);\n"
|
||||||
|
" ivec2 in_coord = (out_coord / regs[0].xy);\n"
|
||||||
|
" ivec2 sample_loc = out_coord % ivec2(regs[0].xy);\n"
|
||||||
|
" int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n"
|
||||||
|
" float frag_depth = texelFetch(fs0, in_coord, sample_index).x;\n"
|
||||||
|
" gl_FragDepth = frag_depth;\n",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)
|
||||||
|
{
|
||||||
|
update_sample_configuration(msaa_image);
|
||||||
|
auto src_view = msaa_image->get_view(0xDEADBEEF, rsx::default_remap_vector);
|
||||||
|
|
||||||
|
overlay_pass::run(
|
||||||
|
cmd,
|
||||||
|
(u16)resolve_image->width(), (u16)resolve_image->height(),
|
||||||
|
resolve_image, src_view,
|
||||||
|
render_pass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct depthonly_unresolve : depth_resolve_base
|
||||||
|
{
|
||||||
|
depthonly_unresolve()
|
||||||
|
{
|
||||||
|
build(
|
||||||
|
" ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n"
|
||||||
|
" pixel_coord *= regs[0].xy;\n"
|
||||||
|
" pixel_coord.x += (gl_SampleID % regs[0].x);\n"
|
||||||
|
" pixel_coord.y += (gl_SampleID / regs[0].x);\n"
|
||||||
|
" float frag_depth = texelFetch(fs0, pixel_coord, 0).x;\n"
|
||||||
|
" gl_FragDepth = frag_depth;\n",
|
||||||
|
"",
|
||||||
|
false,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)
|
||||||
|
{
|
||||||
|
renderpass_config.set_multisample_state(msaa_image->samples(), 0xFFFF, true, false, false);
|
||||||
|
renderpass_config.set_multisample_shading_rate(1.f);
|
||||||
|
update_sample_configuration(msaa_image);
|
||||||
|
|
||||||
|
auto src_view = resolve_image->get_view(0xAAE4, rsx::default_remap_vector);
|
||||||
|
|
||||||
|
overlay_pass::run(
|
||||||
|
cmd,
|
||||||
|
(u16)msaa_image->width(), (u16)msaa_image->height(),
|
||||||
|
msaa_image, src_view,
|
||||||
|
render_pass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct depthstencil_resolve_AMD : depth_resolve_base
|
||||||
|
{
|
||||||
|
depthstencil_resolve_AMD()
|
||||||
|
{
|
||||||
|
renderpass_config.enable_stencil_test(
|
||||||
|
VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, // Always replace
|
||||||
|
VK_COMPARE_OP_ALWAYS, // Always pass
|
||||||
|
0xFF, // Full write-through
|
||||||
|
0); // Unused
|
||||||
|
|
||||||
|
m_num_usable_samplers = 2;
|
||||||
|
|
||||||
|
build(
|
||||||
|
" ivec2 out_coord = ivec2(gl_FragCoord.xy);\n"
|
||||||
|
" ivec2 in_coord = (out_coord / regs[0].xy);\n"
|
||||||
|
" ivec2 sample_loc = out_coord % ivec2(regs[0].xy);\n"
|
||||||
|
" int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n"
|
||||||
|
" float frag_depth = texelFetch(fs0, in_coord, sample_index).x;\n"
|
||||||
|
" uint frag_stencil = texelFetch(fs1, in_coord, sample_index).x;\n"
|
||||||
|
" gl_FragDepth = frag_depth;\n"
|
||||||
|
" gl_FragStencilRefARB = int(frag_stencil);\n",
|
||||||
|
|
||||||
|
"#extension GL_ARB_shader_stencil_export : enable\n",
|
||||||
|
|
||||||
|
true,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)
|
||||||
|
{
|
||||||
|
update_sample_configuration(msaa_image);
|
||||||
|
auto depth_view = msaa_image->get_view(0xDEADBEEF, rsx::default_remap_vector, VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||||
|
auto stencil_view = msaa_image->get_view(0xDEADBEEF, rsx::default_remap_vector, VK_IMAGE_ASPECT_STENCIL_BIT);
|
||||||
|
|
||||||
|
overlay_pass::run(
|
||||||
|
cmd,
|
||||||
|
(u16)resolve_image->width(), (u16)resolve_image->height(),
|
||||||
|
resolve_image, { depth_view, stencil_view },
|
||||||
|
render_pass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct depthstencil_unresolve_AMD : depth_resolve_base
|
||||||
|
{
|
||||||
|
depthstencil_unresolve_AMD()
|
||||||
|
{
|
||||||
|
renderpass_config.enable_stencil_test(
|
||||||
|
VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, // Always replace
|
||||||
|
VK_COMPARE_OP_ALWAYS, // Always pass
|
||||||
|
0xFF, // Full write-through
|
||||||
|
0); // Unused
|
||||||
|
|
||||||
|
m_num_usable_samplers = 2;
|
||||||
|
|
||||||
|
build(
|
||||||
|
" ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n"
|
||||||
|
" pixel_coord *= regs[0].xy;\n"
|
||||||
|
" pixel_coord.x += (gl_SampleID % regs[0].x);\n"
|
||||||
|
" pixel_coord.y += (gl_SampleID / regs[0].x);\n"
|
||||||
|
" float frag_depth = texelFetch(fs0, pixel_coord, 0).x;\n"
|
||||||
|
" uint frag_stencil = texelFetch(fs1, pixel_coord, 0).x;\n"
|
||||||
|
" gl_FragDepth = frag_depth;\n"
|
||||||
|
" gl_FragStencilRefARB = int(frag_stencil);\n",
|
||||||
|
|
||||||
|
"#extension GL_ARB_shader_stencil_export : enable\n",
|
||||||
|
|
||||||
|
true,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)
|
||||||
|
{
|
||||||
|
renderpass_config.set_multisample_state(msaa_image->samples(), 0xFFFF, true, false, false);
|
||||||
|
renderpass_config.set_multisample_shading_rate(1.f);
|
||||||
|
update_sample_configuration(msaa_image);
|
||||||
|
|
||||||
|
auto depth_view = resolve_image->get_view(0xAAE4, rsx::default_remap_vector, VK_IMAGE_ASPECT_DEPTH_BIT);
|
||||||
|
auto stencil_view = resolve_image->get_view(0xAAE4, rsx::default_remap_vector, VK_IMAGE_ASPECT_STENCIL_BIT);
|
||||||
|
|
||||||
|
overlay_pass::run(
|
||||||
|
cmd,
|
||||||
|
(u16)msaa_image->width(), (u16)msaa_image->height(),
|
||||||
|
msaa_image, { depth_view, stencil_view },
|
||||||
|
render_pass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void resolve_image(vk::command_buffer& cmd, vk::viewable_image* dst, vk::viewable_image* src);
|
||||||
|
void unresolve_image(vk::command_buffer& cmd, vk::viewable_image* dst, vk::viewable_image* src);
|
||||||
|
void reset_resolve_resources();
|
||||||
|
void clear_resolve_helpers();
|
||||||
|
}
|
|
@ -577,20 +577,24 @@ namespace rsx
|
||||||
template <typename SurfaceType>
|
template <typename SurfaceType>
|
||||||
std::tuple<u16, u16, u16, u16> get_transferable_region(const SurfaceType* surface)
|
std::tuple<u16, u16, u16, u16> get_transferable_region(const SurfaceType* surface)
|
||||||
{
|
{
|
||||||
const u16 src_w = surface->old_contents.source->width();
|
|
||||||
const u16 src_h = surface->old_contents.source->height();
|
|
||||||
u16 dst_w = src_w;
|
|
||||||
u16 dst_h = src_h;
|
|
||||||
|
|
||||||
auto src = static_cast<const SurfaceType*>(surface->old_contents.source);
|
auto src = static_cast<const SurfaceType*>(surface->old_contents.source);
|
||||||
dst_w = (dst_w * src->samples_x) / surface->samples_x;
|
auto area1 = surface->get_normalized_memory_area();
|
||||||
dst_h = (dst_h * src->samples_y) / surface->samples_y;
|
auto area2 = surface->get_normalized_memory_area();
|
||||||
|
|
||||||
const f32 scale_x = (f32)dst_w / src_w;
|
auto w = std::min(area1.x2, area2.x2);
|
||||||
const f32 scale_y = (f32)dst_h / src_h;
|
auto h = std::min(area1.y2, area2.y2);
|
||||||
|
|
||||||
std::tie(std::ignore, std::ignore, dst_w, dst_h) = clip_region<u16>(dst_w, dst_h, 0, 0, surface->width(), surface->height(), true);
|
const auto src_scale_x = src->get_bpp() * src->samples_x;
|
||||||
return std::make_tuple(u16(dst_w / scale_x), u16(dst_h / scale_y), dst_w, dst_h);
|
const auto src_scale_y = src->samples_y;
|
||||||
|
const auto dst_scale_x = surface->get_bpp() * surface->samples_x;
|
||||||
|
const auto dst_scale_y = surface->samples_y;
|
||||||
|
|
||||||
|
const u16 src_w = u16(w / src_scale_x);
|
||||||
|
const u16 src_h = u16(h / src_scale_y);
|
||||||
|
const u16 dst_w = u16(w / dst_scale_x);
|
||||||
|
const u16 dst_h = u16(h / dst_scale_y);
|
||||||
|
|
||||||
|
return std::make_tuple(src_w, src_h, dst_w, dst_h);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename SurfaceType>
|
template <typename SurfaceType>
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
<ClInclude Include="Emu\RSX\VK\VKProgramBuffer.h" />
|
<ClInclude Include="Emu\RSX\VK\VKProgramBuffer.h" />
|
||||||
<ClInclude Include="Emu\RSX\VK\VKRenderPass.h" />
|
<ClInclude Include="Emu\RSX\VK\VKRenderPass.h" />
|
||||||
<ClInclude Include="Emu\RSX\VK\VKRenderTargets.h" />
|
<ClInclude Include="Emu\RSX\VK\VKRenderTargets.h" />
|
||||||
|
<ClInclude Include="Emu\RSX\VK\VKResolveHelper.h" />
|
||||||
<ClInclude Include="Emu\RSX\VK\VKTextOut.h" />
|
<ClInclude Include="Emu\RSX\VK\VKTextOut.h" />
|
||||||
<ClInclude Include="Emu\RSX\VK\VKTextureCache.h" />
|
<ClInclude Include="Emu\RSX\VK\VKTextureCache.h" />
|
||||||
<ClInclude Include="Emu\RSX\VK\VKVertexProgram.h" />
|
<ClInclude Include="Emu\RSX\VK\VKVertexProgram.h" />
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
<ClCompile Include="Emu\RSX\VK\VKHelpers.cpp" />
|
<ClCompile Include="Emu\RSX\VK\VKHelpers.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\VK\VKProgramPipeline.cpp" />
|
<ClCompile Include="Emu\RSX\VK\VKProgramPipeline.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\VK\VKRenderPass.cpp" />
|
<ClCompile Include="Emu\RSX\VK\VKRenderPass.cpp" />
|
||||||
|
<ClCompile Include="Emu\RSX\VK\VKResolveHelper.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\VK\VKTexture.cpp" />
|
<ClCompile Include="Emu\RSX\VK\VKTexture.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\VK\VKVertexBuffers.cpp" />
|
<ClCompile Include="Emu\RSX\VK\VKVertexBuffers.cpp" />
|
||||||
<ClCompile Include="Emu\RSX\VK\VKVertexProgram.cpp" />
|
<ClCompile Include="Emu\RSX\VK\VKVertexProgram.cpp" />
|
||||||
|
|
|
@ -49,6 +49,9 @@
|
||||||
<ClInclude Include="Emu\RSX\VK\VKRenderPass.h">
|
<ClInclude Include="Emu\RSX\VK\VKRenderPass.h">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Emu\RSX\VK\VKResolveHelper.h">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="Emu\RSX\VK\VKFramebuffer.h">
|
<ClInclude Include="Emu\RSX\VK\VKFramebuffer.h">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -89,6 +92,9 @@
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Emu\RSX\VK\VKRenderPass.cpp">
|
<ClCompile Include="Emu\RSX\VK\VKRenderPass.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClCompile Include="Emu\RSX\VK\VKResolveHelper.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="Emu\RSX\VK\VKFramebuffer.cpp">
|
<ClCompile Include="Emu\RSX\VK\VKFramebuffer.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue