From b8721b64758cc1ce6a2df5365cb0bf10461dd166 Mon Sep 17 00:00:00 2001 From: Vincent Lejeune Date: Wed, 10 Aug 2016 16:52:35 +0200 Subject: [PATCH] gl: Refactor vertex_buffer.cpp --- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 25 +- rpcs3/Emu/RSX/GL/GLGSRender.h | 10 +- rpcs3/Emu/RSX/GL/vertex_buffer.cpp | 446 ++++++++++++++++------------- 3 files changed, 253 insertions(+), 228 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 5046318aee..7334513e9d 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -487,7 +487,9 @@ void GLGSRender::end() } } - u32 offset_in_index_buffer = set_vertex_buffer(); + u32 vertex_draw_count; + std::optional > indexed_draw_info; + std::tie(vertex_draw_count, indexed_draw_info) = set_vertex_buffer(); m_vao.bind(); std::chrono::time_point then = std::chrono::system_clock::now(); @@ -497,26 +499,9 @@ void GLGSRender::end() m_program->validate(); } - if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed) + if (indexed_draw_info) { - rsx::index_array_type indexed_type = rsx::method_registers.index_type(); - - if (indexed_type == rsx::index_array_type::u32) - { - __glcheck glDrawElements(gl::draw_mode(rsx::method_registers.current_draw_clause.primitive), vertex_draw_count, GL_UNSIGNED_INT, (GLvoid *)(std::ptrdiff_t)offset_in_index_buffer); - } - else if (indexed_type == rsx::index_array_type::u16) - { - __glcheck glDrawElements(gl::draw_mode(rsx::method_registers.current_draw_clause.primitive), vertex_draw_count, GL_UNSIGNED_SHORT, (GLvoid *)(std::ptrdiff_t)offset_in_index_buffer); - } - else - { - throw std::logic_error("bad index array type"); - } - } - else if (!gl::is_primitive_native(rsx::method_registers.current_draw_clause.primitive)) - { - __glcheck glDrawElements(gl::draw_mode(rsx::method_registers.current_draw_clause.primitive), vertex_draw_count, GL_UNSIGNED_SHORT, (GLvoid *)(std::ptrdiff_t)offset_in_index_buffer); + __glcheck glDrawElements(gl::draw_mode(rsx::method_registers.current_draw_clause.primitive), vertex_draw_count, std::get<0>(indexed_draw_info.value()), (GLvoid *)(std::ptrdiff_t)std::get<1>(indexed_draw_info.value())); } else { diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 0a6eabde8a..8f3ae3aee0 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -4,6 +4,7 @@ #include "rsx_gl_texture.h" #include "gl_texture_cache.h" #include "gl_render_targets.h" +#include #define RSX_DEBUG 1 @@ -56,7 +57,14 @@ public: private: static u32 enable(u32 enable, u32 cap); static u32 enable(u32 enable, u32 cap, u32 index); - u32 set_vertex_buffer(); + + // Return element to draw and in case of indexed draw index type and offset in index buffer + std::tuple > > set_vertex_buffer(); + + void upload_vertex_buffers(const u32 &max_index, const u32 &max_vertex_attrib_size, const u32 &input_mask, const u32 &texture_index_offset); + + // Returns vertex count + u32 upload_inline_array(const u32 &max_vertex_attrib_size, const u32 &texture_index_offset); public: bool load_program(); diff --git a/rpcs3/Emu/RSX/GL/vertex_buffer.cpp b/rpcs3/Emu/RSX/GL/vertex_buffer.cpp index 38f29097f6..79807cc3af 100644 --- a/rpcs3/Emu/RSX/GL/vertex_buffer.cpp +++ b/rpcs3/Emu/RSX/GL/vertex_buffer.cpp @@ -172,7 +172,20 @@ namespace } } -u32 GLGSRender::set_vertex_buffer() +namespace +{ + GLenum get_index_type(rsx::index_array_type type) + { + switch (type) + { + case rsx::index_array_type::u16: return GL_UNSIGNED_SHORT; + case rsx::index_array_type::u32: return GL_UNSIGNED_INT; + } + throw; + } +} + +std::tuple > > GLGSRender::set_vertex_buffer() { //initialize vertex attributes //merge all vertex arrays @@ -184,12 +197,7 @@ u32 GLGSRender::set_vertex_buffer() u32 input_mask = rsx::method_registers.vertex_attrib_input_mask(); u32 min_index = 0, max_index = 0; u32 max_vertex_attrib_size = 0; - u32 offset_in_index_buffer = 0; - - vertex_draw_count = 0; - - //place holder; replace with actual index buffer - gsl::span index_array; + u32 vertex_or_index_count; for (u8 index = 0; index < rsx::limits::vertex_count; ++index) { @@ -199,225 +207,249 @@ u32 GLGSRender::set_vertex_buffer() } } + std::optional > index_info; + if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed) { rsx::index_array_type type = rsx::method_registers.index_type(); u32 type_size = ::narrow(get_index_type_size(type)); - vertex_draw_count += rsx::method_registers.current_draw_clause.get_elements_count(); + vertex_or_index_count = get_index_count(rsx::method_registers.current_draw_clause.primitive, rsx::method_registers.current_draw_clause.get_elements_count()); - u32 max_size = get_index_count(rsx::method_registers.current_draw_clause.primitive, vertex_draw_count) * type_size; + u32 max_size = vertex_or_index_count * type_size; auto mapping = m_index_ring_buffer.alloc_and_map(max_size); void *ptr = mapping.first; - offset_in_index_buffer = mapping.second; + u32 offset_in_index_buffer = mapping.second; - std::tie(min_index, max_index, vertex_draw_count) = upload_index_buffer(get_raw_index_array(rsx::method_registers.current_draw_clause.first_count_commands), ptr, type, rsx::method_registers.current_draw_clause.primitive, rsx::method_registers.current_draw_clause.first_count_commands, vertex_draw_count); + std::tie(min_index, max_index, vertex_or_index_count) = upload_index_buffer(get_raw_index_array(rsx::method_registers.current_draw_clause.first_count_commands), ptr, type, rsx::method_registers.current_draw_clause.primitive, rsx::method_registers.current_draw_clause.first_count_commands, vertex_or_index_count); m_index_ring_buffer.unmap(); + index_info = std::make_tuple(get_index_type(type), offset_in_index_buffer); + } + else + { + u32 vertex_count; + if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::inlined_array) + { + // We need to go through array to determine vertex count so upload it + vertex_count = upload_inline_array(max_vertex_attrib_size, texture_index_offset); + } + else + { + assert(rsx::method_registers.current_draw_clause.command == rsx::draw_command::array); + vertex_count = rsx::method_registers.current_draw_clause.get_elements_count(); + max_index = vertex_count - 1; + } + + if (!gl::is_primitive_native(rsx::method_registers.current_draw_clause.primitive)) + { + u32 offset_in_index_buffer; + std::tie(vertex_or_index_count, offset_in_index_buffer) = get_index_array_for_emulated_non_indexed_draw(rsx::method_registers.current_draw_clause.first_count_commands, rsx::method_registers.current_draw_clause.primitive, m_index_ring_buffer); + index_info = std::make_tuple(static_cast(GL_UNSIGNED_SHORT), offset_in_index_buffer); + } + else + { + vertex_or_index_count = vertex_count; + } } if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::inlined_array) { - u32 stride = 0; - u32 offsets[rsx::limits::vertex_count] = { 0 }; - - for (u32 i = 0; i < rsx::limits::vertex_count; ++i) - { - const auto &info = rsx::method_registers.vertex_arrays_info[i]; - if (!info.size) continue; - - offsets[i] = stride; - stride += rsx::get_vertex_type_size_on_host(info.type, info.size); - } - - vertex_draw_count = (u32)(inline_vertex_array.size() * sizeof(u32)) / stride; - m_attrib_ring_buffer.reserve_and_map(vertex_draw_count * max_vertex_attrib_size); - - for (int index = 0; index < rsx::limits::vertex_count; ++index) - { - auto &vertex_info = rsx::method_registers.vertex_arrays_info[index]; - - int location; - if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[index] + "_buffer", &location)) - continue; - - if (!vertex_info.size) // disabled, bind a null sampler - { - glActiveTexture(GL_TEXTURE0 + index + texture_index_offset); - glBindTexture(GL_TEXTURE_BUFFER, 0); - glProgramUniform1i(m_program->id(), location, index + texture_index_offset); - continue; - } - - const u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size); - u32 data_size = element_size * vertex_draw_count; - u32 gl_type = to_gl_internal_type(vertex_info.type, vertex_info.size); - - auto &texture = m_gl_attrib_buffers[index]; - - u8 *src = reinterpret_cast(inline_vertex_array.data()); - auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); - u8 *dst = static_cast(mapping.first); - - src += offsets[index]; - prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, vertex_draw_count); - - //TODO: properly handle compressed data - for (u32 i = 0; i < vertex_draw_count; ++i) - { - if (vertex_info.type == rsx::vertex_base_type::ub && vertex_info.size == 4) - { - dst[0] = src[3]; - dst[1] = src[2]; - dst[2] = src[1]; - dst[3] = src[0]; - } - else - memcpy(dst, src, element_size); - - src += stride; - dst += element_size; - } - - texture.copy_from(m_attrib_ring_buffer, gl_type, mapping.second, data_size); - - //Link texture to uniform - m_program->uniforms.texture(location, index + texture_index_offset, texture); - - if (!gl::is_primitive_native(rsx::method_registers.current_draw_clause.primitive)) - { - std::tie(vertex_draw_count, offset_in_index_buffer) = get_index_array_for_emulated_non_indexed_draw({ { 0, vertex_draw_count } }, rsx::method_registers.current_draw_clause.primitive, m_index_ring_buffer); - } - } + // Already uploaded when determining vertex count, we can return here + return std::make_tuple(vertex_or_index_count, index_info); } - if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array) - { - vertex_draw_count += rsx::method_registers.current_draw_clause.get_elements_count(); - } + upload_vertex_buffers(max_index, max_vertex_attrib_size, input_mask, texture_index_offset); - if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array || rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed) - { - u32 verts_allocated = std::max(vertex_draw_count, max_index + 1); - __glcheck m_attrib_ring_buffer.reserve_and_map(verts_allocated * max_vertex_attrib_size); - - for (int index = 0; index < rsx::limits::vertex_count; ++index) - { - int location; - if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[index] + "_buffer", &location)) - continue; - - bool enabled = !!(input_mask & (1 << index)); - if (!enabled) - { - glActiveTexture(GL_TEXTURE0 + index + texture_index_offset); - glBindTexture(GL_TEXTURE_BUFFER, 0); - glProgramUniform1i(m_program->id(), location, index + texture_index_offset); - 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); - //vertex_array.resize(vertex_draw_count * element_size); - - u32 data_size = vertex_draw_count * element_size; - u32 gl_type = to_gl_internal_type(vertex_info.type, vertex_info.size); - auto &texture = m_gl_attrib_buffers[index]; - - u32 buffer_offset = 0; - - // 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(vm::base(address)); - - if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array) - { - auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); - gsl::byte *dst = static_cast(mapping.first); - buffer_offset = mapping.second; - - size_t offset = 0; - gsl::span dest_span(dst, data_size); - prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, vertex_draw_count); - - 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, rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size)); - offset += first_count.second * element_size; - } - } - - if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed) - { - data_size = (max_index + 1) * element_size; - auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); - gsl::byte *dst = static_cast(mapping.first); - buffer_offset = mapping.second; - - gsl::span dest_span(dst, data_size); - prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, vertex_draw_count); - - write_vertex_array_data_to_buffer(dest_span, src_ptr, 0, max_index + 1, vertex_info.type, vertex_info.size, vertex_info.stride, rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size)); - } - - texture.copy_from(m_attrib_ring_buffer, gl_type, buffer_offset, data_size); - - //Link texture to uniform - m_program->uniforms.texture(location, index + texture_index_offset, texture); - } - else if (rsx::method_registers.register_vertex_info[index].size > 0) - { - auto &vertex_info = rsx::method_registers.register_vertex_info[index]; - - switch (vertex_info.type) - { - case rsx::vertex_base_type::f: - { - const u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size); - const u32 gl_type = to_gl_internal_type(vertex_info.type, vertex_info.size); - const size_t data_size = element_size; - - auto &texture = m_gl_attrib_buffers[index]; - - auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); - u8 *dst = static_cast(mapping.first); - - memcpy(dst, vertex_info.data.data(), element_size); - texture.copy_from(m_attrib_ring_buffer, gl_type, mapping.second, data_size); - - //Link texture to uniform - m_program->uniforms.texture(location, index + texture_index_offset, texture); - break; - } - default: - LOG_ERROR(RSX, "bad non array vertex data format (type=%d, size=%d)", (u32)vertex_info.type, vertex_info.size); - break; - } - } - else - { - glActiveTexture(GL_TEXTURE0 + index + texture_index_offset); - glBindTexture(GL_TEXTURE_BUFFER, 0); - glProgramUniform1i(m_program->id(), location, index + texture_index_offset); - continue; - } - } - - if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array && !gl::is_primitive_native(rsx::method_registers.current_draw_clause.primitive)) - { - std::tie(vertex_draw_count, offset_in_index_buffer) = get_index_array_for_emulated_non_indexed_draw(rsx::method_registers.current_draw_clause.first_count_commands, rsx::method_registers.current_draw_clause.primitive, m_index_ring_buffer); - } - } - - m_attrib_ring_buffer.unmap(); std::chrono::time_point now = std::chrono::system_clock::now(); m_vertex_upload_time += std::chrono::duration_cast(now - then).count(); - return offset_in_index_buffer; + return std::make_tuple(vertex_or_index_count, index_info); +} + +void GLGSRender::upload_vertex_buffers(const u32 &max_index, const u32 &max_vertex_attrib_size, const u32 &input_mask, const u32 &texture_index_offset) +{ + u32 verts_allocated = max_index + 1; + __glcheck m_attrib_ring_buffer.reserve_and_map(verts_allocated * max_vertex_attrib_size); + + for (int index = 0; index < rsx::limits::vertex_count; ++index) + { + int location; + if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[index] + "_buffer", &location)) + continue; + + bool enabled = !!(input_mask & (1 << index)); + if (!enabled) + { + glActiveTexture(GL_TEXTURE0 + index + texture_index_offset); + glBindTexture(GL_TEXTURE_BUFFER, 0); + glProgramUniform1i(m_program->id(), location, index + texture_index_offset); + 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); + //vertex_array.resize(vertex_draw_count * element_size); + + u32 data_size = verts_allocated * element_size; + u32 gl_type = to_gl_internal_type(vertex_info.type, vertex_info.size); + auto &texture = m_gl_attrib_buffers[index]; + + u32 buffer_offset = 0; + + // 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(vm::base(address)); + + if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array) + { + auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); + gsl::byte *dst = static_cast(mapping.first); + buffer_offset = mapping.second; + + size_t offset = 0; + gsl::span dest_span(dst, data_size); + prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, verts_allocated); + + 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, rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size)); + offset += first_count.second * element_size; + } + } + + if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed) + { + data_size = (max_index + 1) * element_size; + auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); + gsl::byte *dst = static_cast(mapping.first); + buffer_offset = mapping.second; + + gsl::span dest_span(dst, data_size); + prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, verts_allocated); + + write_vertex_array_data_to_buffer(dest_span, src_ptr, 0, max_index + 1, vertex_info.type, vertex_info.size, vertex_info.stride, rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size)); + } + + texture.copy_from(m_attrib_ring_buffer, gl_type, buffer_offset, data_size); + + //Link texture to uniform + m_program->uniforms.texture(location, index + texture_index_offset, texture); + } + else if (rsx::method_registers.register_vertex_info[index].size > 0) + { + auto &vertex_info = rsx::method_registers.register_vertex_info[index]; + + switch (vertex_info.type) + { + case rsx::vertex_base_type::f: + { + const u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size); + const u32 gl_type = to_gl_internal_type(vertex_info.type, vertex_info.size); + const size_t data_size = element_size; + + auto &texture = m_gl_attrib_buffers[index]; + + auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); + u8 *dst = static_cast(mapping.first); + + memcpy(dst, vertex_info.data.data(), element_size); + texture.copy_from(m_attrib_ring_buffer, gl_type, mapping.second, data_size); + + //Link texture to uniform + m_program->uniforms.texture(location, index + texture_index_offset, texture); + break; + } + default: + LOG_ERROR(RSX, "bad non array vertex data format (type=%d, size=%d)", (u32)vertex_info.type, vertex_info.size); + break; + } + } + else + { + glActiveTexture(GL_TEXTURE0 + index + texture_index_offset); + glBindTexture(GL_TEXTURE_BUFFER, 0); + glProgramUniform1i(m_program->id(), location, index + texture_index_offset); + continue; + } + } + m_attrib_ring_buffer.unmap(); +} + +u32 GLGSRender::upload_inline_array(const u32 &max_vertex_attrib_size, const u32 &texture_index_offset) +{ + u32 stride = 0; + u32 offsets[rsx::limits::vertex_count] = { 0 }; + + for (u32 i = 0; i < rsx::limits::vertex_count; ++i) + { + const auto &info = rsx::method_registers.vertex_arrays_info[i]; + if (!info.size) continue; + + offsets[i] = stride; + stride += rsx::get_vertex_type_size_on_host(info.type, info.size); + } + + u32 vertex_draw_count = (u32)(inline_vertex_array.size() * sizeof(u32)) / stride; + m_attrib_ring_buffer.reserve_and_map(vertex_draw_count * max_vertex_attrib_size); + + for (int index = 0; index < rsx::limits::vertex_count; ++index) + { + auto &vertex_info = rsx::method_registers.vertex_arrays_info[index]; + + int location; + if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[index] + "_buffer", &location)) + continue; + + if (!vertex_info.size) // disabled, bind a null sampler + { + glActiveTexture(GL_TEXTURE0 + index + texture_index_offset); + glBindTexture(GL_TEXTURE_BUFFER, 0); + glProgramUniform1i(m_program->id(), location, index + texture_index_offset); + continue; + } + + const u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size); + u32 data_size = element_size * vertex_draw_count; + u32 gl_type = to_gl_internal_type(vertex_info.type, vertex_info.size); + + auto &texture = m_gl_attrib_buffers[index]; + + u8 *src = reinterpret_cast(inline_vertex_array.data()); + auto mapping = m_attrib_ring_buffer.alloc_from_reserve(data_size, m_min_texbuffer_alignment); + u8 *dst = static_cast(mapping.first); + + src += offsets[index]; + prepare_buffer_for_writing(dst, vertex_info.type, vertex_info.size, vertex_draw_count); + + //TODO: properly handle compressed data + for (u32 i = 0; i < vertex_draw_count; ++i) + { + if (vertex_info.type == rsx::vertex_base_type::ub && vertex_info.size == 4) + { + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + } + else + memcpy(dst, src, element_size); + + src += stride; + dst += element_size; + } + + texture.copy_from(m_attrib_ring_buffer, gl_type, mapping.second, data_size); + + //Link texture to uniform + m_program->uniforms.texture(location, index + texture_index_offset, texture); + m_attrib_ring_buffer.unmap(); + } + return vertex_draw_count; }