vk: refactor VKVertexBuffers.

This commit is contained in:
Vincent Lejeune 2016-08-04 23:21:45 +02:00
parent c382ce5260
commit a94b9d38b0
3 changed files with 278 additions and 266 deletions

View File

@ -673,14 +673,17 @@ void VKGSRender::end()
vkCmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_program->pipeline);
vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets, 0, nullptr);
if (!std::get<1>(upload_info))
vkCmdDraw(m_command_buffer, vertex_draw_count, 1, 0, 0);
std::optional<std::tuple<VkDeviceSize, VkIndexType> > index_info = std::get<2>(upload_info);
if (!index_info)
vkCmdDraw(m_command_buffer, std::get<1>(upload_info), 1, 0, 0);
else
{
VkIndexType index_type;
u32 index_count;
u32 index_count = std::get<1>(upload_info);
VkDeviceSize offset;
std::tie(std::ignore, std::ignore, index_count, offset, index_type) = upload_info;
std::tie(offset, index_type) = index_info.value();
vkCmdBindIndexBuffer(m_command_buffer, m_index_buffer_ring_info.heap->value, offset, index_type);
vkCmdDrawIndexed(m_command_buffer, index_count, 1, 0, 0, 0);

View File

@ -4,6 +4,7 @@
#include "VKTextureCache.h"
#include "VKRenderTargets.h"
#include "VKFormats.h"
#include <Utilities\optional.hpp>
#define RSX_DEBUG 1
@ -85,7 +86,24 @@ private:
void sync_at_semaphore_release();
void prepare_rtts();
/// returns primitive topology, is_indexed, index_count, offset in index buffer, index type
std::tuple<VkPrimitiveTopology, bool, u32, VkDeviceSize, VkIndexType> upload_vertex_data();
std::tuple<VkPrimitiveTopology, u32, std::optional<std::tuple<VkDeviceSize, VkIndexType> > > upload_vertex_data();
void upload_vertex_buffers(u32 input_mask, u32 vertex_max_index);
/// returns number of vertex drawn
u32 upload_inlined_array();
/**
* Upload index (and expands it if primitive type is not natively supported).
* Returns min index, max index, index_count, and (offset_in_index_buffer, index_type)
*/
std::tuple<u32, u32, u32, std::tuple<VkDeviceSize, VkIndexType>> upload_index_buffer(const rsx::draw_clause &clause);
/**
* Creates and fills an index buffer emulating unsupported primitive type.
* Returns index_count and (offset_in_index_buffer, index_type)
*/
std::tuple<u32, std::tuple<VkDeviceSize, VkIndexType> > generate_emulating_index_buffer(const rsx::draw_clause &clause);
public:
bool load_program();

View File

@ -204,33 +204,11 @@ namespace vk
}
throw;
}
std::tuple<u32, u32, VkIndexType> upload_index_buffer(gsl::span<const gsl::byte> raw_index_buffer, rsx::primitive_type type, rsx::index_array_type index_type, void *dst_ptr, bool indexed_draw, u32 vertex_count, u32 index_count, std::vector<std::pair<u32, u32>> first_count_commands)
{
bool emulated = false;
get_appropriate_topology(type, emulated);
if (indexed_draw)
{
u32 min_index, max_index;
size_t index_size = (index_type == rsx::index_array_type::u32) ? 4 : 2;
std::tie(min_index, max_index) = write_index_array_data_to_buffer(gsl::span<gsl::byte>(static_cast<gsl::byte*>(dst_ptr), vertex_count * index_size), raw_index_buffer,
index_type, type, rsx::method_registers.restart_index_enabled(), rsx::method_registers.restart_index(), first_count_commands,
[](auto prim) { return !is_primitive_native(prim); });
return std::make_tuple(min_index, max_index, get_index_type(index_type));
}
write_index_array_for_non_indexed_non_native_primitive_to_buffer(reinterpret_cast<char*>(dst_ptr), type, 0, vertex_count);
return std::make_tuple(0, vertex_count-1, VK_INDEX_TYPE_UINT16);
}
}
std::tuple<VkPrimitiveTopology, bool, u32, VkDeviceSize, VkIndexType>
VKGSRender::upload_vertex_data()
namespace
{
//initialize vertex attributes
const std::string reg_table[] =
static constexpr std::array<const char*, 16> s_reg_table =
{
"in_pos_buffer", "in_weight_buffer", "in_normal_buffer",
"in_diff_color_buffer", "in_spec_color_buffer",
@ -239,62 +217,173 @@ VKGSRender::upload_vertex_data()
"in_tc0_buffer", "in_tc1_buffer", "in_tc2_buffer", "in_tc3_buffer",
"in_tc4_buffer", "in_tc5_buffer", "in_tc6_buffer", "in_tc7_buffer"
};
}
std::tuple<VkPrimitiveTopology, u32, std::optional<std::tuple<VkDeviceSize, VkIndexType> > >
VKGSRender::upload_vertex_data()
{
u32 input_mask = rsx::method_registers.vertex_attrib_input_mask();
size_t offset_in_index_buffer = -1;
vertex_draw_count = 0;
u32 min_index, max_index;
bool is_indexed_draw = (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed);
bool primitives_emulated = false;
u32 index_count = 0;
VkIndexType index_format = VK_INDEX_TYPE_UINT16;
std::optional<std::tuple<VkDeviceSize, VkIndexType> > index_info;
if (is_indexed_draw)
{
std::tie(min_index, max_index, index_count, index_info) = upload_index_buffer(rsx::method_registers.current_draw_clause);
}
bool primitives_emulated = false;
VkPrimitiveTopology prims = vk::get_appropriate_topology(rsx::method_registers.current_draw_clause.primitive, primitives_emulated);
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array)
{
vertex_draw_count += rsx::method_registers.current_draw_clause.get_elements_count();
}
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed || primitives_emulated)
{
rsx::index_array_type type = rsx::method_registers.index_type();
u32 type_size = ::narrow<u32>(get_index_type_size(type));
if (is_indexed_draw) //Could be emulated or not, emulated array vertex count already computed above
{
vertex_draw_count += rsx::method_registers.current_draw_clause.get_elements_count();
}
index_count = vertex_draw_count;
u32 upload_size = vertex_draw_count * type_size;
std::vector<std::pair<u32, u32>> ranges = rsx::method_registers.current_draw_clause.first_count_commands;
if (primitives_emulated)
{
index_count = get_index_count(rsx::method_registers.current_draw_clause.primitive, vertex_draw_count);
upload_size = index_count * sizeof(u16);
if (is_indexed_draw)
std::tie(index_count, index_info) = generate_emulating_index_buffer(rsx::method_registers.current_draw_clause);
}
else
{
ranges.resize(0);
ranges.push_back(std::pair<u32, u32>(0, vertex_draw_count));
}
index_count = rsx::method_registers.current_draw_clause.get_elements_count();
}
offset_in_index_buffer = m_index_buffer_ring_info.alloc<256>(upload_size);
void* buf = m_index_buffer_ring_info.map(offset_in_index_buffer, upload_size);
std::tie(min_index, max_index, index_format) = vk::upload_index_buffer(get_raw_index_array(ranges), rsx::method_registers.current_draw_clause.primitive, type, buf, is_indexed_draw, vertex_draw_count, index_count, ranges);
m_index_buffer_ring_info.unmap();
is_indexed_draw = true;
min_index = 0;
max_index = rsx::method_registers.current_draw_clause.get_elements_count() - 1;
}
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::inlined_array)
{
index_count = upload_inlined_array();
}
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array || rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed)
{
upload_vertex_buffers(input_mask, max_index);
}
return std::make_tuple(prims, index_count, index_info);
}
void VKGSRender::upload_vertex_buffers(u32 input_mask, u32 vertex_max_index)
{
for (int index = 0; index < rsx::limits::vertex_count; ++index)
{
bool enabled = !!(input_mask & (1 << index));
if (!m_program->has_uniform(s_reg_table[index]))
continue;
if (!enabled)
{
continue;
}
if (rsx::method_registers.vertex_arrays_info[index].size > 0)
{
auto &vertex_info = rsx::method_registers.vertex_arrays_info[index];
// Fill vertex_array
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size);
u32 real_element_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size);
u32 upload_size = real_element_size * (vertex_max_index + 1);
bool requires_expansion = vk::requires_component_expansion(vertex_info.type, vertex_info.size);
// Get source pointer
u32 base_offset = rsx::method_registers.vertex_data_base_offset();
u32 offset = rsx::method_registers.vertex_arrays_info[index].offset();
u32 address = base_offset + rsx::get_address(offset & 0x7fffffff, offset >> 31);
const gsl::byte *src_ptr = gsl::narrow_cast<const gsl::byte*>(vm::base(address));
u32 num_stored_verts = vertex_max_index + 1;
VkDeviceSize offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(upload_size);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, upload_size);
vk::prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, vertex_max_index + 1);
gsl::span<gsl::byte> dest_span(static_cast<gsl::byte*>(dst), upload_size);
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array)
{
VkDeviceSize offset = 0;
for (const auto &first_count : rsx::method_registers.current_draw_clause.first_count_commands)
{
write_vertex_array_data_to_buffer(dest_span.subspan(offset), src_ptr, first_count.first, first_count.second, vertex_info.type, vertex_info.size, vertex_info.stride, real_element_size);
offset += first_count.second * real_element_size;
}
}
else if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed)
{
write_vertex_array_data_to_buffer(dest_span, src_ptr, 0, vertex_max_index + 1, vertex_info.type, vertex_info.size, vertex_info.stride, real_element_size);
}
m_attrib_ring_info.unmap();
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size);
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(*m_device, m_attrib_ring_info.heap->value, format, offset_in_attrib_buffer, upload_size));
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, s_reg_table[index], descriptor_sets);
}
else if (rsx::method_registers.register_vertex_info[index].size > 0)
{
//Untested!
auto &vertex_info = rsx::method_registers.register_vertex_info[index];
switch (vertex_info.type)
{
case rsx::vertex_base_type::f:
{
size_t data_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size);
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size);
u32 offset_in_attrib_buffer = 0;
void *data_ptr = vertex_info.data.data();
if (vk::requires_component_expansion(vertex_info.type, vertex_info.size))
{
const u32 num_stored_verts = static_cast<u32>(data_size / (sizeof(float) * vertex_info.size));
const u32 real_element_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size);
data_size = real_element_size * num_stored_verts;
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(data_size);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, data_size);
vk::expand_array_components<float, 3, 4, 1>(reinterpret_cast<float*>(vertex_info.data.data()), dst, num_stored_verts);
m_attrib_ring_info.unmap();
}
else
{
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(data_size);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, data_size);
memcpy(dst, vertex_info.data.data(), data_size);
m_attrib_ring_info.unmap();
}
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(*m_device, m_attrib_ring_info.heap->value, format, offset_in_attrib_buffer, data_size));
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, s_reg_table[index], descriptor_sets);
break;
}
default:
fmt::throw_exception("Unknown base type %d" HERE, (u32)vertex_info.type);
}
}
else
{
//This section should theoretically be unreachable (data stream without available data)
//Variable is defined in the shaders but no data is available
//Bind a buffer view to keep the driver from crashing if access is attempted.
u32 offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(32);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, 32);
memset(dst, 0, 32);
m_attrib_ring_info.unmap();
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(*m_device, m_attrib_ring_info.heap->value, VK_FORMAT_R32_SFLOAT, offset_in_attrib_buffer, 32));
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, s_reg_table[index], descriptor_sets);
}
}
}
u32 VKGSRender::upload_inlined_array()
{
u32 stride = 0;
u32 offsets[rsx::limits::vertex_count] = { 0 };
@ -308,13 +397,13 @@ VKGSRender::upload_vertex_data()
stride += rsx::get_vertex_type_size_on_host(info.type, info.size);
}
vertex_draw_count = (u32)(inline_vertex_array.size() * sizeof(u32)) / stride;
u32 vertex_draw_count = (u32)(inline_vertex_array.size() * sizeof(u32)) / stride;
for (int index = 0; index < rsx::limits::vertex_count; ++index)
{
auto &vertex_info = rsx::method_registers.vertex_arrays_info[index];
if (!m_program->has_uniform(reg_table[index]))
if (!m_program->has_uniform(s_reg_table[index]))
continue;
if (!vertex_info.size) // disabled
@ -363,142 +452,44 @@ VKGSRender::upload_vertex_data()
m_attrib_ring_info.unmap();
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(*m_device, m_attrib_ring_info.heap->value, format, offset_in_attrib_buffer, data_size));
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, reg_table[index], descriptor_sets);
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, s_reg_table[index], descriptor_sets);
}
return vertex_draw_count;
}
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array || rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed)
std::tuple<u32, u32, u32, std::tuple<VkDeviceSize, VkIndexType>> VKGSRender::upload_index_buffer(const rsx::draw_clause &clause)
{
for (int index = 0; index < rsx::limits::vertex_count; ++index)
rsx::index_array_type index_type = rsx::method_registers.index_type();
u32 type_size = gsl::narrow<u32>(get_index_type_size(index_type));
u32 index_count = get_index_count(clause.primitive, clause.get_elements_count());
u32 upload_size = index_count * type_size;
VkDeviceSize offset_in_index_buffer = m_index_buffer_ring_info.alloc<256>(upload_size);
void* buf = m_index_buffer_ring_info.map(offset_in_index_buffer, upload_size);
u32 min_index, max_index;
std::tie(min_index, max_index) = write_index_array_data_to_buffer(gsl::span<gsl::byte>(static_cast<gsl::byte*>(buf), index_count * type_size), get_raw_index_array(clause.first_count_commands),
index_type, clause.primitive, rsx::method_registers.restart_index_enabled(), rsx::method_registers.restart_index(), clause.first_count_commands,
[](auto prim) { return !is_primitive_native(prim); });
m_index_buffer_ring_info.unmap();
return std::make_tuple(min_index, max_index, index_count, std::make_tuple(offset_in_index_buffer, vk::get_index_type(index_type)));
}
std::tuple<u32, std::tuple<VkDeviceSize, VkIndexType> > VKGSRender::generate_emulating_index_buffer(const rsx::draw_clause &clause)
{
bool enabled = !!(input_mask & (1 << index));
u32 vertex_count = clause.get_elements_count();
if (!m_program->has_uniform(reg_table[index]))
continue;
u32 index_count = get_index_count(clause.primitive, vertex_count);
u32 upload_size = index_count * sizeof(u16);
if (!enabled)
{
continue;
VkDeviceSize offset_in_index_buffer = m_index_buffer_ring_info.alloc<256>(upload_size);
void* buf = m_index_buffer_ring_info.map(offset_in_index_buffer, upload_size);
write_index_array_for_non_indexed_non_native_primitive_to_buffer(reinterpret_cast<char*>(buf), clause.primitive, 0, vertex_count);
m_index_buffer_ring_info.unmap();
return std::make_tuple(index_count, std::make_tuple(offset_in_index_buffer, VK_INDEX_TYPE_UINT16));
}
if (rsx::method_registers.vertex_arrays_info[index].size > 0)
{
auto &vertex_info = rsx::method_registers.vertex_arrays_info[index];
// Fill vertex_array
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size);
u32 real_element_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size);
u32 upload_size = real_element_size * vertex_draw_count;
u32 offset_in_attrib_buffer = 0;
bool requires_expansion = vk::requires_component_expansion(vertex_info.type, vertex_info.size);
// Get source pointer
u32 base_offset = rsx::method_registers.vertex_data_base_offset();
u32 offset = rsx::method_registers.vertex_arrays_info[index].offset();
u32 address = base_offset + rsx::get_address(offset & 0x7fffffff, offset >> 31);
const gsl::byte *src_ptr = gsl::narrow_cast<const gsl::byte*>(vm::base(address));
u32 num_stored_verts = vertex_draw_count;
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array)
{
size_t offset = 0;
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(upload_size);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, upload_size);
vk::prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, vertex_draw_count);
gsl::span<gsl::byte> dest_span(static_cast<gsl::byte*>(dst), upload_size);
for (const auto &first_count : rsx::method_registers.current_draw_clause.first_count_commands)
{
write_vertex_array_data_to_buffer(dest_span.subspan(offset), src_ptr, first_count.first, first_count.second, vertex_info.type, vertex_info.size, vertex_info.stride, real_element_size);
offset += first_count.second * real_element_size;
}
m_attrib_ring_info.unmap();
}
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed)
{
num_stored_verts = (max_index + 1);
upload_size = real_element_size * num_stored_verts;
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(upload_size);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, upload_size);
gsl::span<gsl::byte> dest_span(static_cast<gsl::byte*>(dst), upload_size);
vk::prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, num_stored_verts);
write_vertex_array_data_to_buffer(dest_span, src_ptr, 0, max_index + 1, vertex_info.type, vertex_info.size, vertex_info.stride, real_element_size);
m_attrib_ring_info.unmap();
}
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size);
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(*m_device, m_attrib_ring_info.heap->value, format, offset_in_attrib_buffer, upload_size));
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, reg_table[index], descriptor_sets);
}
else if (rsx::method_registers.register_vertex_info[index].size > 0)
{
//Untested!
auto &vertex_info = rsx::method_registers.register_vertex_info[index];
switch (vertex_info.type)
{
case rsx::vertex_base_type::f:
{
size_t data_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size);
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size);
u32 offset_in_attrib_buffer = 0;
void *data_ptr = vertex_info.data.data();
if (vk::requires_component_expansion(vertex_info.type, vertex_info.size))
{
const u32 num_stored_verts = static_cast<u32>(data_size / (sizeof(float) * vertex_info.size));
const u32 real_element_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size);
data_size = real_element_size * num_stored_verts;
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(data_size);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, data_size);
vk::expand_array_components<float, 3, 4, 1>(reinterpret_cast<float*>(vertex_info.data.data()), dst, num_stored_verts);
m_attrib_ring_info.unmap();
}
else
{
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(data_size);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, data_size);
memcpy(dst, vertex_info.data.data(), data_size);
m_attrib_ring_info.unmap();
}
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(*m_device, m_attrib_ring_info.heap->value, format, offset_in_attrib_buffer, data_size));
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, reg_table[index], descriptor_sets);
break;
}
default:
LOG_ERROR(RSX, "bad non array vertex data format (type=%d, size=%d)", (u32)vertex_info.type, vertex_info.size);
break;
}
}
else
{
//This section should theoretically be unreachable (data stream without available data)
//Variable is defined in the shaders but no data is available
//Bind a buffer view to keep the driver from crashing if access is attempted.
u32 offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(32);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, 32);
memset(dst, 0, 32);
m_attrib_ring_info.unmap();
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(*m_device, m_attrib_ring_info.heap->value, VK_FORMAT_R32_SFLOAT, offset_in_attrib_buffer, 32));
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, reg_table[index], descriptor_sets);
}
}
}
return std::make_tuple(prims, is_indexed_draw, index_count, offset_in_index_buffer, index_format);
}