mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-30 21:32:50 +00:00
rsx/common/d3d12/gl: Support for CMP/non pow of 2 size vertex formats.
Also use class enum for base_vertex_type everywhere. Fix Bomberman Ultra color and Cubixx HD geometry.
This commit is contained in:
parent
3afc62e525
commit
d153575e59
@ -5,6 +5,27 @@
|
||||
#define MIN2(x, y) ((x) < (y)) ? (x) : (y)
|
||||
#define MAX2(x, y) ((x) > (y)) ? (x) : (y)
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Convert CMP vector to RGBA16.
|
||||
* A vector in CMP (compressed) format is stored as X11Y11Z10 and has a W component of 1.
|
||||
* X11 and Y11 channels are int between -1024 and 1023 interpreted as -1.f, 1.f
|
||||
* Z10 is int between -512 and 511 interpreted as -1.f, 1.f
|
||||
*/
|
||||
std::array<u16, 4> decode_cmp_vector(u32 encoded_vector)
|
||||
{
|
||||
u16 Z = encoded_vector >> 22;
|
||||
Z = Z << 6;
|
||||
u16 Y = (encoded_vector >> 11) & 0x7FF;
|
||||
Y = Y << 5;
|
||||
u16 X = encoded_vector & 0x7FF;
|
||||
X = X << 5;
|
||||
return{ X, Y, Z, 1 };
|
||||
}
|
||||
}
|
||||
|
||||
void write_vertex_array_data_to_buffer(void *buffer, u32 first, u32 count, size_t index, const rsx::data_array_format_info &vertex_array_desc)
|
||||
{
|
||||
assert(vertex_array_desc.size > 0);
|
||||
@ -15,8 +36,7 @@ void write_vertex_array_data_to_buffer(void *buffer, u32 first, u32 count, size_
|
||||
u32 offset = rsx::method_registers[NV4097_SET_VERTEX_DATA_ARRAY_OFFSET + index];
|
||||
u32 address = rsx::get_address(offset & 0x7fffffff, offset >> 31);
|
||||
|
||||
u32 type_size = rsx::get_vertex_type_size(vertex_array_desc.type);
|
||||
u32 element_size = type_size * vertex_array_desc.size;
|
||||
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_array_desc.type, vertex_array_desc.size);
|
||||
|
||||
u32 base_offset = rsx::method_registers[NV4097_SET_VERTEX_DATA_BASE_OFFSET];
|
||||
u32 base_index = rsx::method_registers[NV4097_SET_VERTEX_DATA_BASE_INDEX];
|
||||
@ -26,13 +46,14 @@ void write_vertex_array_data_to_buffer(void *buffer, u32 first, u32 count, size_
|
||||
auto src = vm::ps3::_ptr<const u8>(address + base_offset + vertex_array_desc.stride * (first + i + base_index));
|
||||
u8* dst = (u8*)buffer + i * element_size;
|
||||
|
||||
switch (type_size)
|
||||
switch (vertex_array_desc.type)
|
||||
{
|
||||
case 1:
|
||||
case Vertex_base_type::ub:
|
||||
memcpy(dst, src, vertex_array_desc.size);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
case Vertex_base_type::s1:
|
||||
case Vertex_base_type::sf:
|
||||
{
|
||||
auto* c_src = (const be_t<u16>*)src;
|
||||
u16* c_dst = (u16*)dst;
|
||||
@ -41,10 +62,14 @@ void write_vertex_array_data_to_buffer(void *buffer, u32 first, u32 count, size_
|
||||
{
|
||||
*c_dst++ = *c_src++;
|
||||
}
|
||||
if (vertex_array_desc.size * sizeof(u16) < element_size)
|
||||
*c_dst++ = 0x3800;
|
||||
break;
|
||||
}
|
||||
|
||||
case 4:
|
||||
case Vertex_base_type::f:
|
||||
case Vertex_base_type::s32k:
|
||||
case Vertex_base_type::ub256:
|
||||
{
|
||||
auto* c_src = (const be_t<u32>*)src;
|
||||
u32* c_dst = (u32*)dst;
|
||||
@ -55,6 +80,17 @@ void write_vertex_array_data_to_buffer(void *buffer, u32 first, u32 count, size_
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Vertex_base_type::cmp:
|
||||
{
|
||||
auto* c_src = (const be_t<u32>*)src;
|
||||
const auto& decoded_vector = decode_cmp_vector(*c_src);
|
||||
u16* c_dst = (u16*)dst;
|
||||
c_dst[0] = decoded_vector[0];
|
||||
c_dst[1] = decoded_vector[1];
|
||||
c_dst[2] = decoded_vector[2];
|
||||
c_dst[3] = decoded_vector[3];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,8 +60,7 @@ std::vector<D3D12_VERTEX_BUFFER_VIEW> D3D12GSRender::upload_vertex_attributes(co
|
||||
// Active vertex array
|
||||
const rsx::data_array_format_info &info = vertex_arrays_info[index];
|
||||
|
||||
u32 type_size = rsx::get_vertex_type_size(info.type);
|
||||
u32 element_size = type_size * info.size;
|
||||
u32 element_size = rsx::get_vertex_type_size_on_host(info.type, info.size);
|
||||
|
||||
size_t buffer_size = element_size * vertex_count;
|
||||
assert(m_vertex_index_data.can_alloc(buffer_size));
|
||||
@ -104,8 +103,7 @@ std::vector<D3D12_VERTEX_BUFFER_VIEW> D3D12GSRender::upload_vertex_attributes(co
|
||||
|
||||
const std::vector<u8> &data = register_vertex_data[index];
|
||||
|
||||
u32 type_size = rsx::get_vertex_type_size(info.type);
|
||||
u32 element_size = type_size * info.size;
|
||||
u32 element_size = rsx::get_vertex_type_size_on_host(info.type, info.size);
|
||||
|
||||
size_t buffer_size = data.size();
|
||||
assert(m_vertex_index_data.can_alloc(buffer_size));
|
||||
@ -253,7 +251,7 @@ std::tuple<D3D12_VERTEX_BUFFER_VIEW, size_t> D3D12GSRender::upload_inlined_verte
|
||||
IAElement.InstanceDataStepRate = 0;
|
||||
m_IASet.push_back(IAElement);
|
||||
|
||||
offset += rsx::get_vertex_type_size(info.type) * info.size;
|
||||
offset += rsx::get_vertex_type_size_on_host(info.type, info.size);
|
||||
}
|
||||
|
||||
// Copy inline buffer
|
||||
|
@ -372,11 +372,11 @@ DXGI_FORMAT get_index_type(u8 index_type)
|
||||
throw EXCEPTION("Invalid index_type (0x%x)", index_type);
|
||||
}
|
||||
|
||||
DXGI_FORMAT get_vertex_attribute_format(u8 type, u8 size)
|
||||
DXGI_FORMAT get_vertex_attribute_format(Vertex_base_type type, u8 size)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CELL_GCM_VERTEX_S1:
|
||||
case Vertex_base_type::s1:
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
@ -387,7 +387,7 @@ DXGI_FORMAT get_vertex_attribute_format(u8 type, u8 size)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GCM_VERTEX_F:
|
||||
case Vertex_base_type::f:
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
@ -398,7 +398,7 @@ DXGI_FORMAT get_vertex_attribute_format(u8 type, u8 size)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GCM_VERTEX_SF:
|
||||
case Vertex_base_type::sf:
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
@ -409,7 +409,7 @@ DXGI_FORMAT get_vertex_attribute_format(u8 type, u8 size)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GCM_VERTEX_UB:
|
||||
case Vertex_base_type::ub:
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
@ -420,7 +420,7 @@ DXGI_FORMAT get_vertex_attribute_format(u8 type, u8 size)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GCM_VERTEX_S32K:
|
||||
case Vertex_base_type::s32k:
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
@ -431,18 +431,18 @@ DXGI_FORMAT get_vertex_attribute_format(u8 type, u8 size)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GCM_VERTEX_CMP:
|
||||
case Vertex_base_type::cmp:
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 1: return DXGI_FORMAT_R32_FLOAT;
|
||||
case 2: return DXGI_FORMAT_R32G32_FLOAT;
|
||||
case 3: return DXGI_FORMAT_R32G32B32_FLOAT;
|
||||
case 4: return DXGI_FORMAT_R32G32B32A32_FLOAT;
|
||||
case 1: return DXGI_FORMAT_R16G16B16A16_SNORM;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4: throw EXCEPTION("Unsupported CMP vertex format with size > 1");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_GCM_VERTEX_UB256:
|
||||
case Vertex_base_type::ub256:
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
|
@ -101,7 +101,7 @@ DXGI_FORMAT get_index_type(u8 index_type);
|
||||
/**
|
||||
* Convert vertex attribute format and size to DXGI_FORMAT
|
||||
*/
|
||||
DXGI_FORMAT get_vertex_attribute_format(u8 type, u8 size);
|
||||
DXGI_FORMAT get_vertex_attribute_format(Vertex_base_type type, u8 size);
|
||||
|
||||
/**
|
||||
* Convert scissor register value to D3D12_RECT
|
||||
|
@ -719,6 +719,21 @@ namespace
|
||||
};
|
||||
}
|
||||
|
||||
Vertex_base_type to_vertex_base_type(u8 in)
|
||||
{
|
||||
switch (in)
|
||||
{
|
||||
case 1: return Vertex_base_type::s1;
|
||||
case 2: return Vertex_base_type::f;
|
||||
case 3: return Vertex_base_type::sf;
|
||||
case 4: return Vertex_base_type::ub;
|
||||
case 5: return Vertex_base_type::s32k;
|
||||
case 6: return Vertex_base_type::cmp;
|
||||
case 7: return Vertex_base_type::ub256;
|
||||
}
|
||||
throw new EXCEPTION("Unknow vertex base type %d", in);
|
||||
}
|
||||
|
||||
std::string rsx::get_method_name(const u32 id)
|
||||
{
|
||||
auto found = methods.find(id);
|
||||
@ -926,17 +941,16 @@ namespace
|
||||
|
||||
std::string get_vertex_attribute_format(u8 type)
|
||||
{
|
||||
switch (type)
|
||||
switch (to_vertex_base_type(type))
|
||||
{
|
||||
case CELL_GCM_VERTEX_S1: return "Short";
|
||||
case CELL_GCM_VERTEX_F: return "Float";
|
||||
case CELL_GCM_VERTEX_SF: return "Half float";
|
||||
case CELL_GCM_VERTEX_UB: return "Unsigned byte";
|
||||
case CELL_GCM_VERTEX_S32K: return "Signed int";
|
||||
case CELL_GCM_VERTEX_CMP: return "CMP";
|
||||
case CELL_GCM_VERTEX_UB256: return "UB256";
|
||||
case Vertex_base_type::s1: return "Short";
|
||||
case Vertex_base_type::f: return "Float";
|
||||
case Vertex_base_type::sf: return "Half float";
|
||||
case Vertex_base_type::ub: return "Unsigned byte";
|
||||
case Vertex_base_type::s32k: return "Signed int";
|
||||
case Vertex_base_type::cmp: return "CMP";
|
||||
case Vertex_base_type::ub256: return "UB256";
|
||||
}
|
||||
return "Error";
|
||||
}
|
||||
|
||||
std::string unpack_vertex_format(u32 arg)
|
||||
|
@ -23,17 +23,19 @@ enum
|
||||
CELL_GCM_DISPLAY_FREQUENCY_DISABLE = 3,
|
||||
};
|
||||
|
||||
enum
|
||||
enum class Vertex_base_type
|
||||
{
|
||||
CELL_GCM_VERTEX_S1 = 1,
|
||||
CELL_GCM_VERTEX_F = 2,
|
||||
CELL_GCM_VERTEX_SF = 3,
|
||||
CELL_GCM_VERTEX_UB = 4,
|
||||
CELL_GCM_VERTEX_S32K = 5,
|
||||
CELL_GCM_VERTEX_CMP = 6,
|
||||
CELL_GCM_VERTEX_UB256 = 7,
|
||||
s1, ///< signed byte
|
||||
f, ///< float
|
||||
sf, ///< half float
|
||||
ub, ///< unsigned byte
|
||||
s32k, ///< signed 32bits int
|
||||
cmp, ///< compressed aka X11G11Z10 and always 1. W.
|
||||
ub256,
|
||||
};
|
||||
|
||||
Vertex_base_type to_vertex_base_type(u8 in);
|
||||
|
||||
enum
|
||||
{
|
||||
CELL_GCM_DRAW_INDEX_ARRAY_TYPE_32 = 0,
|
||||
|
@ -292,6 +292,39 @@ void apply_attrib_array(gl::glsl::program& program, int location, const std::vec
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
gl::buffer_pointer::type gl_types(Vertex_base_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Vertex_base_type::s1: return gl::buffer_pointer::type::s16;
|
||||
case Vertex_base_type::f: return gl::buffer_pointer::type::f32;
|
||||
case Vertex_base_type::sf: return gl::buffer_pointer::type::f16;
|
||||
case Vertex_base_type::ub: return gl::buffer_pointer::type::u8;
|
||||
case Vertex_base_type::s32k: return gl::buffer_pointer::type::s32;
|
||||
case Vertex_base_type::cmp: return gl::buffer_pointer::type::s16; // Needs conversion
|
||||
case Vertex_base_type::ub256: gl::buffer_pointer::type::u8;
|
||||
}
|
||||
}
|
||||
|
||||
bool gl_normalized(Vertex_base_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case Vertex_base_type::s1:
|
||||
case Vertex_base_type::ub:
|
||||
case Vertex_base_type::cmp:
|
||||
return true;
|
||||
case Vertex_base_type::f:
|
||||
case Vertex_base_type::sf:
|
||||
case Vertex_base_type::ub256:
|
||||
case Vertex_base_type::s32k:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLGSRender::end()
|
||||
{
|
||||
if (!draw_fbo || !vertex_draw_count)
|
||||
@ -322,31 +355,9 @@ void GLGSRender::end()
|
||||
}
|
||||
|
||||
//initialize vertex attributes
|
||||
static const gl::buffer_pointer::type gl_types[] =
|
||||
{
|
||||
gl::buffer_pointer::type::f32,
|
||||
|
||||
gl::buffer_pointer::type::s16,
|
||||
gl::buffer_pointer::type::f32,
|
||||
gl::buffer_pointer::type::f16,
|
||||
gl::buffer_pointer::type::u8,
|
||||
gl::buffer_pointer::type::s16,
|
||||
gl::buffer_pointer::type::f32, // Needs conversion
|
||||
gl::buffer_pointer::type::u8
|
||||
};
|
||||
|
||||
static const bool gl_normalized[] =
|
||||
{
|
||||
false,
|
||||
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
};
|
||||
|
||||
//merge all vertex arrays
|
||||
std::vector<u8> vertex_arrays_data;
|
||||
@ -382,8 +393,8 @@ void GLGSRender::end()
|
||||
|
||||
__glcheck m_program->attribs[location] =
|
||||
(m_vao + offset)
|
||||
.config(gl_types[vertex_info.type], vertex_info.size, gl_normalized[vertex_info.type]);
|
||||
offset += rsx::get_vertex_type_size(vertex_info.type) * vertex_info.size;
|
||||
.config(gl_types(vertex_info.type), vertex_info.size, gl_normalized(vertex_info.type));
|
||||
offset += rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -416,7 +427,7 @@ void GLGSRender::end()
|
||||
|
||||
__glcheck m_program->attribs[location] =
|
||||
(m_vao + vertex_arrays_offsets[index])
|
||||
.config(gl_types[vertex_info.type], vertex_info.size, gl_normalized[vertex_info.type]);
|
||||
.config(gl_types(vertex_info.type), vertex_info.size, gl_normalized(vertex_info.type));
|
||||
}
|
||||
else if (register_vertex_info[index].size > 0)
|
||||
{
|
||||
@ -425,7 +436,7 @@ void GLGSRender::end()
|
||||
|
||||
switch (vertex_info.type)
|
||||
{
|
||||
case CELL_GCM_VERTEX_F:
|
||||
case Vertex_base_type::f:
|
||||
switch (register_vertex_info[index].size)
|
||||
{
|
||||
case 1: apply_attrib_array<f32, 1>(*m_program, location, vertex_data); break;
|
||||
|
@ -120,22 +120,51 @@ namespace rsx
|
||||
return res;
|
||||
}
|
||||
|
||||
u32 get_vertex_type_size(u32 type)
|
||||
u32 get_vertex_type_size_on_host(Vertex_base_type type, u32 size)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CELL_GCM_VERTEX_S1: return sizeof(u16);
|
||||
case CELL_GCM_VERTEX_F: return sizeof(f32);
|
||||
case CELL_GCM_VERTEX_SF: return sizeof(f16);
|
||||
case CELL_GCM_VERTEX_UB: return sizeof(u8);
|
||||
case CELL_GCM_VERTEX_S32K: return sizeof(u32);
|
||||
case CELL_GCM_VERTEX_CMP: return sizeof(u32);
|
||||
case CELL_GCM_VERTEX_UB256: return sizeof(u8) * 4;
|
||||
case Vertex_base_type::s1:
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
return sizeof(u16) * size;
|
||||
case 3:
|
||||
return sizeof(u16) * 4;
|
||||
}
|
||||
throw new EXCEPTION("Wrong vector size");
|
||||
case Vertex_base_type::f: return sizeof(f32) * size;
|
||||
case Vertex_base_type::sf:
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
return sizeof(f16) * size;
|
||||
case 3:
|
||||
return sizeof(f16) * 4;
|
||||
}
|
||||
throw new EXCEPTION("Wrong vector size");
|
||||
case Vertex_base_type::ub:
|
||||
switch (size)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
return sizeof(u8) * size;
|
||||
case 3:
|
||||
return sizeof(u8) * 4;
|
||||
}
|
||||
throw new EXCEPTION("Wrong vector size");
|
||||
case Vertex_base_type::s32k: return sizeof(u32) * size;
|
||||
case Vertex_base_type::cmp: return sizeof(u16) * 4;
|
||||
case Vertex_base_type::ub256: return sizeof(u8) * 4;
|
||||
|
||||
default:
|
||||
LOG_ERROR(RSX, "RSXVertexData::GetTypeSize: Bad vertex data type (%d)!", type);
|
||||
assert(0);
|
||||
return 1;
|
||||
throw new EXCEPTION("RSXVertexData::GetTypeSize: Bad vertex data type (%d)!", type);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,8 +283,7 @@ namespace rsx
|
||||
|
||||
auto &data = vertex_arrays[index];
|
||||
|
||||
u32 type_size = get_vertex_type_size(info.type);
|
||||
u32 element_size = type_size * info.size;
|
||||
u32 element_size = get_vertex_type_size_on_host(info.type, info.size);
|
||||
|
||||
u32 dst_position = (u32)data.size();
|
||||
data.resize(dst_position + count * element_size);
|
||||
@ -537,10 +565,9 @@ namespace rsx
|
||||
if (!info.size) // disabled
|
||||
continue;
|
||||
|
||||
u32 type_size = rsx::get_vertex_type_size(info.type);
|
||||
u32 element_size = type_size * info.size;
|
||||
u32 element_size = rsx::get_vertex_type_size_on_host(info.type, info.size);
|
||||
|
||||
if (type_size == 1 && info.size == 4)
|
||||
if (info.type == Vertex_base_type::ub && info.size == 4)
|
||||
{
|
||||
dst[0] = src[3];
|
||||
dst[1] = src[2];
|
||||
|
@ -148,7 +148,7 @@ namespace rsx
|
||||
static std::string path_to_root();
|
||||
};
|
||||
|
||||
u32 get_vertex_type_size(u32 type);
|
||||
u32 get_vertex_type_size_on_host(Vertex_base_type type, u32 size);
|
||||
|
||||
u32 get_address(u32 offset, u32 location);
|
||||
|
||||
@ -195,14 +195,14 @@ namespace rsx
|
||||
u16 frequency = 0;
|
||||
u8 stride = 0;
|
||||
u8 size = 0;
|
||||
u8 type = CELL_GCM_VERTEX_F;
|
||||
Vertex_base_type type = Vertex_base_type::f;
|
||||
|
||||
void unpack_array(u32 data_array_format)
|
||||
{
|
||||
frequency = data_array_format >> 16;
|
||||
stride = (data_array_format >> 8) & 0xff;
|
||||
size = (data_array_format >> 4) & 0xf;
|
||||
type = data_array_format & 0xf;
|
||||
type = to_vertex_base_type(data_array_format & 0xf);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -14,10 +14,10 @@ namespace rsx
|
||||
rsx_method_t methods[0x10000 >> 2]{};
|
||||
|
||||
template<typename Type> struct vertex_data_type_from_element_type;
|
||||
template<> struct vertex_data_type_from_element_type<float> { enum { type = CELL_GCM_VERTEX_F }; };
|
||||
template<> struct vertex_data_type_from_element_type<f16> { enum { type = CELL_GCM_VERTEX_SF }; };
|
||||
template<> struct vertex_data_type_from_element_type<u8> { enum { type = CELL_GCM_VERTEX_UB }; };
|
||||
template<> struct vertex_data_type_from_element_type<u16> { enum { type = CELL_GCM_VERTEX_S1 }; };
|
||||
template<> struct vertex_data_type_from_element_type<float> { static const Vertex_base_type type = Vertex_base_type::f; };
|
||||
template<> struct vertex_data_type_from_element_type<f16> { static const Vertex_base_type type = Vertex_base_type::sf; };
|
||||
template<> struct vertex_data_type_from_element_type<u8> { static const Vertex_base_type type = Vertex_base_type::ub; };
|
||||
template<> struct vertex_data_type_from_element_type<u16> { static const Vertex_base_type type = Vertex_base_type::s1; };
|
||||
|
||||
namespace nv406e
|
||||
{
|
||||
@ -248,7 +248,7 @@ namespace rsx
|
||||
continue;
|
||||
|
||||
u32 count = u32(rsx->register_vertex_data[i].size()) /
|
||||
rsx::get_vertex_type_size(rsx->register_vertex_info[i].type) * rsx->register_vertex_info[i].size;
|
||||
rsx::get_vertex_type_size_on_host(rsx->register_vertex_info[i].type, rsx->register_vertex_info[i].size);
|
||||
|
||||
if (count < min_count)
|
||||
min_count = count;
|
||||
|
Loading…
x
Reference in New Issue
Block a user