rsx: use range for vertex buffer attribute.

This commit is contained in:
Vincent Lejeune 2016-08-21 19:17:09 +02:00
parent c24fba89e8
commit 42b518cf7e
10 changed files with 347 additions and 251 deletions

View File

@ -32,12 +32,12 @@ namespace
return{ X, Y, Z, 1 }; return{ X, Y, Z, 1 };
} }
template<typename U, typename T> template <typename U, typename T>
void copy_whole_attribute_array(gsl::span<T> dst, const gsl::byte* src_ptr, u8 attribute_size, u8 dst_stride, u32 src_stride, u32 first, u32 vertex_count) void copy_whole_attribute_array(gsl::span<T> dst, gsl::span<const gsl::byte> src_ptr, u8 attribute_size, u8 dst_stride, u32 src_stride, u32 vertex_count)
{ {
for (u32 vertex = 0; vertex < vertex_count; ++vertex) for (u32 vertex = 0; vertex < vertex_count; ++vertex)
{ {
const U* src = reinterpret_cast<const U*>(src_ptr + src_stride * (first + vertex)); gsl::span<const U> src = gsl::as_span<const U>(src_ptr.subspan(src_stride * vertex, attribute_size * sizeof(const U)));
for (u32 i = 0; i < attribute_size; ++i) for (u32 i = 0; i < attribute_size; ++i)
{ {
dst[vertex * dst_stride / sizeof(T) + i] = src[i]; dst[vertex * dst_stride / sizeof(T) + i] = src[i];
@ -46,7 +46,7 @@ namespace
} }
} }
void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, const gsl::byte *src_ptr, u32 first, u32 count, rsx::vertex_base_type type, u32 vector_element_count, u32 attribute_src_stride, u8 dst_stride) void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, gsl::span<const gsl::byte> src_ptr, u32 count, rsx::vertex_base_type type, u32 vector_element_count, u32 attribute_src_stride, u8 dst_stride)
{ {
verify(HERE), (vector_element_count > 0); verify(HERE), (vector_element_count > 0);
@ -56,7 +56,7 @@ void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, const
case rsx::vertex_base_type::ub256: case rsx::vertex_base_type::ub256:
{ {
gsl::span<u8> dst_span = as_span_workaround<u8>(raw_dst_span); gsl::span<u8> dst_span = as_span_workaround<u8>(raw_dst_span);
copy_whole_attribute_array<u8>(dst_span, src_ptr, vector_element_count, dst_stride, attribute_src_stride, first, count); copy_whole_attribute_array<u8>(dst_span, src_ptr, vector_element_count, dst_stride, attribute_src_stride, count);
return; return;
} }
case rsx::vertex_base_type::s1: case rsx::vertex_base_type::s1:
@ -64,13 +64,13 @@ void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, const
case rsx::vertex_base_type::s32k: case rsx::vertex_base_type::s32k:
{ {
gsl::span<u16> dst_span = as_span_workaround<u16>(raw_dst_span); gsl::span<u16> dst_span = as_span_workaround<u16>(raw_dst_span);
copy_whole_attribute_array<be_t<u16>>(dst_span, src_ptr, vector_element_count, dst_stride, attribute_src_stride, first, count); copy_whole_attribute_array<be_t<u16>>(dst_span, src_ptr, vector_element_count, dst_stride, attribute_src_stride, count);
return; return;
} }
case rsx::vertex_base_type::f: case rsx::vertex_base_type::f:
{ {
gsl::span<u32> dst_span = as_span_workaround<u32>(raw_dst_span); gsl::span<u32> dst_span = as_span_workaround<u32>(raw_dst_span);
copy_whole_attribute_array<be_t<u32>>(dst_span, src_ptr, vector_element_count, dst_stride, attribute_src_stride, first, count); copy_whole_attribute_array<be_t<u32>>(dst_span, src_ptr, vector_element_count, dst_stride, attribute_src_stride, count);
return; return;
} }
case rsx::vertex_base_type::cmp: case rsx::vertex_base_type::cmp:
@ -78,8 +78,11 @@ void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, const
gsl::span<u16> dst_span = as_span_workaround<u16>(raw_dst_span); gsl::span<u16> dst_span = as_span_workaround<u16>(raw_dst_span);
for (u32 i = 0; i < count; ++i) for (u32 i = 0; i < count; ++i)
{ {
auto* c_src = (const be_t<u32>*)(src_ptr + attribute_src_stride * (first + i)); be_t<u32> src_value;
const auto& decoded_vector = decode_cmp_vector(*c_src); memcpy(&src_value,
src_ptr.subspan(attribute_src_stride * i).data(),
sizeof(be_t<u32>));
const auto& decoded_vector = decode_cmp_vector(src_value);
dst_span[i * dst_stride / sizeof(u16)] = decoded_vector[0]; dst_span[i * dst_stride / sizeof(u16)] = decoded_vector[0];
dst_span[i * dst_stride / sizeof(u16) + 1] = decoded_vector[1]; dst_span[i * dst_stride / sizeof(u16) + 1] = decoded_vector[1];
dst_span[i * dst_stride / sizeof(u16) + 2] = decoded_vector[2]; dst_span[i * dst_stride / sizeof(u16) + 2] = decoded_vector[2];

View File

@ -7,10 +7,10 @@
#include "../RSXThread.h" #include "../RSXThread.h"
/** /**
* Write count vertex attributes from src_ptr starting at first. * Write count vertex attributes from src_ptr.
* src_ptr array layout is deduced from the type, vector element count and src_stride arguments. * src_ptr array layout is deduced from the type, vector element count and src_stride arguments.
*/ */
void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, const gsl::byte *src_ptr, u32 first, u32 count, rsx::vertex_base_type type, u32 vector_element_count, u32 attribute_src_stride, u8 dst_stride); void write_vertex_array_data_to_buffer(gsl::span<gsl::byte> raw_dst_span, gsl::span<const gsl::byte> src_ptr, u32 count, rsx::vertex_base_type type, u32 vector_element_count, u32 attribute_src_stride, u8 dst_stride);
/* /*
* If primitive mode is not supported and need to be emulated (using an index buffer) returns false. * If primitive mode is not supported and need to be emulated (using an index buffer) returns false.

View File

@ -7,6 +7,7 @@
#include "../Common/BufferUtils.h" #include "../Common/BufferUtils.h"
#include "D3D12Formats.h" #include "D3D12Formats.h"
#include "../rsx_methods.h" #include "../rsx_methods.h"
#include <Utilities/variant.hpp>
namespace namespace
{ {
@ -80,77 +81,90 @@ namespace
} }
} }
namespace
std::vector<D3D12_SHADER_RESOURCE_VIEW_DESC> D3D12GSRender::upload_vertex_attributes(
const std::vector<std::pair<u32, u32> > &vertex_ranges,
gsl::not_null<ID3D12GraphicsCommandList*> command_list)
{ {
struct vertex_buffer_visitor
{
std::vector<D3D12_SHADER_RESOURCE_VIEW_DESC> vertex_buffer_views; std::vector<D3D12_SHADER_RESOURCE_VIEW_DESC> vertex_buffer_views;
command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertex_buffer_data.Get(), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, D3D12_RESOURCE_STATE_COPY_DEST));
u32 vertex_count = get_vertex_count(vertex_ranges); vertex_buffer_visitor(u32 vtx_cnt, ID3D12GraphicsCommandList* cmdlst, ID3D12Resource* write_vertex_buffer,
size_t offset_in_vertex_buffers_buffer = 0; d3d12_data_heap& heap)
u32 input_mask = rsx::method_registers.vertex_attrib_input_mask(); : vertex_count(vtx_cnt)
verify(HERE), rsx::method_registers.vertex_data_base_index() == 0; , offset_in_vertex_buffers_buffer(0)
, m_buffer_data(heap)
for (int index = 0; index < rsx::limits::vertex_count; ++index) , command_list(cmdlst)
, m_vertex_buffer_data(write_vertex_buffer)
{ {
bool enabled = !!(input_mask & (1 << index)); }
if (!enabled)
continue;
if (rsx::method_registers.vertex_arrays_info[index].size > 0) void operator()(const rsx::vertex_array_buffer& vertex_array)
{ {
// Active vertex array u32 element_size = rsx::get_vertex_type_size_on_host(vertex_array.type, vertex_array.attribute_size);
const rsx::data_array_format_info &info = rsx::method_registers.vertex_arrays_info[index];
u32 element_size = rsx::get_vertex_type_size_on_host(info.type, info.size);
UINT buffer_size = element_size * vertex_count; UINT buffer_size = element_size * vertex_count;
size_t heap_offset = m_buffer_data.alloc<D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT>(buffer_size); size_t heap_offset = m_buffer_data.alloc<D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT>(buffer_size);
u32 base_offset = rsx::method_registers.vertex_data_base_offset(); void* mapped_buffer = m_buffer_data.map<void>(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size));
u32 offset = rsx::method_registers.vertex_arrays_info[index].offset(); gsl::span<gsl::byte> mapped_buffer_span = {(gsl::byte*)mapped_buffer, gsl::narrow_cast<int>(buffer_size)};
u32 address = base_offset + rsx::get_address(offset & 0x7fffffff, offset >> 31); write_vertex_array_data_to_buffer(mapped_buffer_span, vertex_array.data, vertex_count, vertex_array.type, vertex_array.attribute_size, vertex_array.stride, element_size);
const gsl::byte *src_ptr = gsl::narrow_cast<const gsl::byte*>(vm::base(address));
void *mapped_buffer = m_buffer_data.map<void>(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size));
for (const auto &range : vertex_ranges)
{
gsl::span<gsl::byte> mapped_buffer_span = { (gsl::byte*)mapped_buffer, gsl::narrow_cast<int>(buffer_size) };
write_vertex_array_data_to_buffer(mapped_buffer_span, src_ptr, range.first, range.second, info.type, info.size, info.stride, element_size);
mapped_buffer = (char*)mapped_buffer + range.second * element_size;
}
m_buffer_data.unmap(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size)); m_buffer_data.unmap(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size));
command_list->CopyBufferRegion(m_vertex_buffer_data.Get(), offset_in_vertex_buffers_buffer, m_buffer_data.get_heap(), heap_offset, buffer_size); command_list->CopyBufferRegion(m_vertex_buffer_data, offset_in_vertex_buffers_buffer, m_buffer_data.get_heap(), heap_offset, buffer_size);
vertex_buffer_views.emplace_back(get_vertex_attribute_srv(info, offset_in_vertex_buffers_buffer, buffer_size)); vertex_buffer_views.emplace_back(get_vertex_attribute_srv(vertex_array.type, vertex_array.attribute_size, offset_in_vertex_buffers_buffer, buffer_size));
offset_in_vertex_buffers_buffer = get_next_multiple_of<48>(offset_in_vertex_buffers_buffer + buffer_size); // 48 is multiple of 2, 4, 6, 8, 12, 16 offset_in_vertex_buffers_buffer = get_next_multiple_of<48>(offset_in_vertex_buffers_buffer + buffer_size); // 48 is multiple of 2, 4, 6, 8, 12, 16
m_timers.buffer_upload_size += buffer_size; //m_timers.buffer_upload_size += buffer_size;
} }
else if (rsx::method_registers.register_vertex_info[index].size > 0)
{
// In register vertex attribute
const rsx::register_vertex_data_info &info = rsx::method_registers.register_vertex_info[index];
u32 element_size = rsx::get_vertex_type_size_on_host(info.type, info.size); void operator()(const rsx::vertex_array_register& vertex_register)
{
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_register.type, vertex_register.attribute_size);
UINT buffer_size = element_size; UINT buffer_size = element_size;
size_t heap_offset = m_buffer_data.alloc<D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT>(buffer_size); size_t heap_offset = m_buffer_data.alloc<D3D12_CONSTANT_BUFFER_DATA_PLACEMENT_ALIGNMENT>(buffer_size);
void *mapped_buffer = m_buffer_data.map<void>(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size)); void* mapped_buffer = m_buffer_data.map<void>(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size));
memcpy(mapped_buffer, info.data.data(), buffer_size); memcpy(mapped_buffer, vertex_register.data.data(), buffer_size);
m_buffer_data.unmap(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size)); m_buffer_data.unmap(CD3DX12_RANGE(heap_offset, heap_offset + buffer_size));
command_list->CopyBufferRegion(m_vertex_buffer_data.Get(), offset_in_vertex_buffers_buffer, m_buffer_data.get_heap(), heap_offset, buffer_size); command_list->CopyBufferRegion(m_vertex_buffer_data, offset_in_vertex_buffers_buffer, m_buffer_data.get_heap(), heap_offset, buffer_size);
vertex_buffer_views.emplace_back(get_vertex_attribute_srv(info.type, info.size, offset_in_vertex_buffers_buffer, buffer_size)); vertex_buffer_views.emplace_back(get_vertex_attribute_srv(vertex_register.type, vertex_register.attribute_size, offset_in_vertex_buffers_buffer, buffer_size));
offset_in_vertex_buffers_buffer = get_next_multiple_of<48>(offset_in_vertex_buffers_buffer + buffer_size); // 48 is multiple of 2, 4, 6, 8, 12, 16 offset_in_vertex_buffers_buffer = get_next_multiple_of<48>(offset_in_vertex_buffers_buffer + buffer_size); // 48 is multiple of 2, 4, 6, 8, 12, 16
} }
void operator()(const rsx::empty_vertex_array& vbo)
{
} }
protected:
u32 vertex_count;
ID3D12GraphicsCommandList* command_list;
ID3D12Resource* m_vertex_buffer_data;
size_t offset_in_vertex_buffers_buffer;
d3d12_data_heap& m_buffer_data;
};
} // End anonymous namespace
std::vector<D3D12_SHADER_RESOURCE_VIEW_DESC> D3D12GSRender::upload_vertex_attributes(
const std::vector<std::pair<u32, u32>>& vertex_ranges,
gsl::not_null<ID3D12GraphicsCommandList*> command_list)
{
command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertex_buffer_data.Get(), D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, D3D12_RESOURCE_STATE_COPY_DEST));
u32 vertex_count = get_vertex_count(vertex_ranges);
verify(HERE), rsx::method_registers.vertex_data_base_index() == 0;
vertex_buffer_visitor visitor(vertex_count, command_list, m_vertex_buffer_data.Get(), m_buffer_data);
const auto& vertex_buffers = get_vertex_buffers(rsx::method_registers, vertex_ranges);
for (const auto& vbo : vertex_buffers)
std::apply_visitor(visitor, vbo);
command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertex_buffer_data.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)); command_list->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertex_buffer_data.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER));
return vertex_buffer_views; return visitor.vertex_buffer_views;
} }
namespace namespace

View File

@ -61,7 +61,7 @@ private:
// Return element to draw and in case of indexed draw index type and offset in index buffer // Return element to draw and in case of indexed draw index type and offset in index buffer
std::tuple<u32, std::optional<std::tuple<GLenum, u32> > > set_vertex_buffer(); std::tuple<u32, std::optional<std::tuple<GLenum, u32> > > 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); void upload_vertex_buffers(u32 min_index, u32 max_index, const u32& max_vertex_attrib_size, const u32& texture_index_offset);
// Returns vertex count // Returns vertex count
u32 upload_inline_array(const u32 &max_vertex_attrib_size, const u32 &texture_index_offset); u32 upload_inline_array(const u32 &max_vertex_attrib_size, const u32 &texture_index_offset);

View File

@ -222,7 +222,7 @@ std::tuple<u32, std::optional<std::tuple<GLenum, u32> > > GLGSRender::set_vertex
u32 offset_in_index_buffer = mapping.second; u32 offset_in_index_buffer = mapping.second;
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); 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);
min_index = 0; // we must keep index to vertex mapping
m_index_ring_buffer.unmap(); m_index_ring_buffer.unmap();
index_info = std::make_tuple(get_index_type(type), offset_in_index_buffer); index_info = std::make_tuple(get_index_type(type), offset_in_index_buffer);
} }
@ -238,7 +238,8 @@ std::tuple<u32, std::optional<std::tuple<GLenum, u32> > > GLGSRender::set_vertex
{ {
assert(rsx::method_registers.current_draw_clause.command == rsx::draw_command::array); assert(rsx::method_registers.current_draw_clause.command == rsx::draw_command::array);
vertex_count = rsx::method_registers.current_draw_clause.get_elements_count(); vertex_count = rsx::method_registers.current_draw_clause.get_elements_count();
max_index = vertex_count - 1; min_index = rsx::method_registers.current_draw_clause.first_count_commands.front().first;
max_index = vertex_count - 1 + min_index;
} }
if (!gl::is_primitive_native(rsx::method_registers.current_draw_clause.primitive)) if (!gl::is_primitive_native(rsx::method_registers.current_draw_clause.primitive))
@ -259,7 +260,7 @@ std::tuple<u32, std::optional<std::tuple<GLenum, u32> > > GLGSRender::set_vertex
return std::make_tuple(vertex_or_index_count, index_info); return std::make_tuple(vertex_or_index_count, index_info);
} }
upload_vertex_buffers(max_index, max_vertex_attrib_size, input_mask, texture_index_offset); upload_vertex_buffers(min_index, max_index, max_vertex_attrib_size, texture_index_offset);
std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now(); std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();
m_vertex_upload_time += std::chrono::duration_cast<std::chrono::microseconds>(now - then).count(); m_vertex_upload_time += std::chrono::duration_cast<std::chrono::microseconds>(now - then).count();
@ -267,118 +268,126 @@ std::tuple<u32, std::optional<std::tuple<GLenum, u32> > > GLGSRender::set_vertex
return std::make_tuple(vertex_or_index_count, index_info); 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) namespace
{ {
u32 verts_allocated = max_index + 1; struct vertex_buffer_visitor
__glcheck m_attrib_ring_buffer.reserve_and_map(verts_allocated * max_vertex_attrib_size); {
vertex_buffer_visitor(u32 vtx_cnt,
u32 texture_idx_offset,
gl::ring_buffer& heap, gl::glsl::program* prog,
gl::texture* attrib_buffer,
u32 min_texbuffer_offset)
: vertex_count(vtx_cnt)
, m_attrib_ring_info(heap)
, m_program(prog)
, texture_index_offset(texture_idx_offset)
, m_gl_attrib_buffers(attrib_buffer)
, m_min_texbuffer_alignment(min_texbuffer_offset)
{
}
void operator()(const rsx::vertex_array_buffer& vertex_array)
{
int location;
if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[vertex_array.index] + "_buffer", &location))
return;
// Fill vertex_array
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_array.type, vertex_array.attribute_size);
u32 data_size = vertex_count * element_size;
u32 gl_type = to_gl_internal_type(vertex_array.type, vertex_array.attribute_size);
auto& texture = m_gl_attrib_buffers[vertex_array.index];
u32 buffer_offset = 0;
auto mapping = m_attrib_ring_info.alloc_from_reserve(data_size, m_min_texbuffer_alignment);
gsl::byte* dst = static_cast<gsl::byte*>(mapping.first);
buffer_offset = mapping.second;
gsl::span<gsl::byte> dest_span(dst, data_size);
prepare_buffer_for_writing(dst, vertex_array.type, vertex_array.attribute_size, vertex_count);
write_vertex_array_data_to_buffer(dest_span, vertex_array.data, vertex_count, vertex_array.type, vertex_array.attribute_size, vertex_array.stride, rsx::get_vertex_type_size_on_host(vertex_array.type, vertex_array.attribute_size));
texture.copy_from(m_attrib_ring_info, gl_type, buffer_offset, data_size);
//Link texture to uniform
m_program->uniforms.texture(location, vertex_array.index + texture_index_offset, texture);
}
void operator()(const rsx::vertex_array_register& vertex_register)
{
int location;
if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[vertex_register.index] + "_buffer", &location))
return;
switch (vertex_register.type)
{
case rsx::vertex_base_type::f:
{
const u32 element_size = rsx::get_vertex_type_size_on_host(vertex_register.type, vertex_register.attribute_size);
const u32 gl_type = to_gl_internal_type(vertex_register.type, vertex_register.attribute_size);
const size_t data_size = element_size;
auto& texture = m_gl_attrib_buffers[vertex_register.index];
auto mapping = m_attrib_ring_info.alloc_from_reserve(data_size, m_min_texbuffer_alignment);
u8 *dst = static_cast<u8*>(mapping.first);
memcpy(dst, vertex_register.data.data(), element_size);
texture.copy_from(m_attrib_ring_info, gl_type, mapping.second, data_size);
//Link texture to uniform
m_program->uniforms.texture(location, vertex_register.index + texture_index_offset, texture);
break;
}
default:
LOG_ERROR(RSX, "bad non array vertex data format (type=%d, size=%d)", (u32)vertex_register.type, vertex_register.attribute_size);
break;
}
}
void operator()(const rsx::empty_vertex_array& vbo)
{
int location;
if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[vbo.index] + "_buffer", &location))
return;
glActiveTexture(GL_TEXTURE0 + vbo.index + texture_index_offset);
glBindTexture(GL_TEXTURE_BUFFER, 0);
glProgramUniform1i(m_program->id(), location, vbo.index + texture_index_offset);
}
protected:
u32 vertex_count;
gl::ring_buffer& m_attrib_ring_info;
gl::glsl::program* m_program;
u32 texture_index_offset;
gl::texture* m_gl_attrib_buffers;
GLint m_min_texbuffer_alignment;
};
} // End anonymous namespace
void GLGSRender::upload_vertex_buffers(u32 min_index, u32 max_index, const u32& max_vertex_attrib_size, const u32& texture_index_offset)
{
u32 verts_allocated = max_index - min_index + 1;
__glcheck m_attrib_ring_buffer.reserve_and_map(verts_allocated * max_vertex_attrib_size);
// Disable texture then reenable them
// Is it really necessary ?
for (int index = 0; index < rsx::limits::vertex_count; ++index) for (int index = 0; index < rsx::limits::vertex_count; ++index)
{ {
int location; int location;
if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[index] + "_buffer", &location)) if (!m_program->uniforms.has_location(rsx::vertex_program::input_attrib_names[index] + "_buffer", &location))
continue; continue;
bool enabled = !!(input_mask & (1 << index));
if (!enabled)
{
glActiveTexture(GL_TEXTURE0 + index + texture_index_offset); glActiveTexture(GL_TEXTURE0 + index + texture_index_offset);
glBindTexture(GL_TEXTURE_BUFFER, 0); glBindTexture(GL_TEXTURE_BUFFER, 0);
glProgramUniform1i(m_program->id(), location, index + texture_index_offset); glProgramUniform1i(m_program->id(), location, index + texture_index_offset);
continue; continue;
} }
vertex_buffer_visitor visitor(verts_allocated, texture_index_offset, m_attrib_ring_buffer, m_program, m_gl_attrib_buffers, m_min_texbuffer_alignment);
if (rsx::method_registers.vertex_arrays_info[index].size > 0) const auto& vertex_buffers = get_vertex_buffers(rsx::method_registers, { {min_index, verts_allocated } });
{ for (const auto& vbo : vertex_buffers)
auto &vertex_info = rsx::method_registers.vertex_arrays_info[index]; std::apply_visitor(visitor, vbo);
// 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<const gsl::byte*>(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<gsl::byte*>(mapping.first);
buffer_offset = mapping.second;
size_t offset = 0;
gsl::span<gsl::byte> 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<gsl::byte*>(mapping.first);
buffer_offset = mapping.second;
gsl::span<gsl::byte> 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<u8*>(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(); m_attrib_ring_buffer.unmap();
} }

View File

@ -583,6 +583,58 @@ namespace rsx
return{ ptr, count * type_size }; return{ ptr, count * type_size };
} }
gsl::span<const gsl::byte> thread::get_raw_vertex_buffer(const rsx::data_array_format_info& vertex_array_info, u32 base_offset, const std::vector<std::pair<u32, u32>>& vertex_ranges) const
{
u32 offset = vertex_array_info.offset();
u32 address = base_offset + rsx::get_address(offset & 0x7fffffff, offset >> 31);
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_array_info.type, vertex_array_info.size);
// Disjoint first_counts ranges not supported atm
for (int i = 0; i < vertex_ranges.size() - 1; i++)
{
const std::tuple<u32, u32>& range = vertex_ranges[i];
const std::tuple<u32, u32>& next_range = vertex_ranges[i + 1];
verify(HERE), (std::get<0>(range) + std::get<1>(range) == std::get<0>(next_range));
}
u32 first = std::get<0>(vertex_ranges.front());
u32 count = std::get<0>(vertex_ranges.back()) + std::get<1>(vertex_ranges.back()) - first;
const gsl::byte* ptr = gsl::narrow_cast<const gsl::byte*>(vm::base(address));
return {ptr + first * vertex_array_info.stride, count * vertex_array_info.stride + element_size};
}
std::vector<std::variant<vertex_array_buffer, vertex_array_register, empty_vertex_array>> thread::get_vertex_buffers(const rsx::rsx_state& state, const std::vector<std::pair<u32, u32>>& vertex_ranges) const
{
std::vector<std::variant<vertex_array_buffer, vertex_array_register, empty_vertex_array>> result;
u32 input_mask = state.vertex_attrib_input_mask();
for (u8 index = 0; index < rsx::limits::vertex_count; ++index)
{
bool enabled = !!(input_mask & (1 << index));
if (!enabled)
continue;
if (state.vertex_arrays_info[index].size > 0)
{
const rsx::data_array_format_info& info = state.vertex_arrays_info[index];
result.push_back(vertex_array_buffer{info.type, info.size, info.stride,
get_raw_vertex_buffer(info, state.vertex_data_base_offset(), vertex_ranges), index});
continue;
}
if (state.register_vertex_info[index].size > 0)
{
const rsx::register_vertex_data_info& info = state.register_vertex_info[index];
result.push_back(vertex_array_register{info.type, info.size, info.data, index});
continue;
}
result.push_back(empty_vertex_array{index});
}
return result;
}
void thread::do_internal_task() void thread::do_internal_task()
{ {
if (m_internal_tasks.empty()) if (m_internal_tasks.empty())

View File

@ -17,6 +17,7 @@
#include "Utilities/Timer.h" #include "Utilities/Timer.h"
#include "Utilities/geometry.h" #include "Utilities/geometry.h"
#include "rsx_trace.h" #include "rsx_trace.h"
#include "Utilities/variant.hpp"
extern u64 get_system_time(); extern u64 get_system_time();
@ -143,6 +144,28 @@ namespace rsx
} }
}; };
struct vertex_array_buffer
{
rsx::vertex_base_type type;
u8 attribute_size;
u8 stride;
gsl::span<const gsl::byte> data;
u8 index;
};
struct vertex_array_register
{
rsx::vertex_base_type type;
u8 attribute_size;
std::array<u32, 4> data;
u8 index;
};
struct empty_vertex_array
{
u8 index;
};
class thread : public named_thread class thread : public named_thread
{ {
std::shared_ptr<thread_ctrl> m_vblank_thread; std::shared_ptr<thread_ctrl> m_vblank_thread;
@ -237,6 +260,9 @@ namespace rsx
virtual bool on_access_violation(u32 address, bool is_writing) { return false; } virtual bool on_access_violation(u32 address, bool is_writing) { return false; }
gsl::span<const gsl::byte> get_raw_index_array(const std::vector<std::pair<u32, u32> >& draw_indexed_clause) const; gsl::span<const gsl::byte> get_raw_index_array(const std::vector<std::pair<u32, u32> >& draw_indexed_clause) const;
gsl::span<const gsl::byte> get_raw_vertex_buffer(const rsx::data_array_format_info&, u32 base_offset, const std::vector<std::pair<u32, u32>>& vertex_ranges) const;
std::vector<std::variant<vertex_array_buffer, vertex_array_register, empty_vertex_array>> get_vertex_buffers(const rsx::rsx_state& state, const std::vector<std::pair<u32, u32>>& vertex_ranges) const;
private: private:
std::mutex m_mtx_task; std::mutex m_mtx_task;

View File

@ -88,7 +88,7 @@ private:
/// returns primitive topology, is_indexed, index_count, offset in index buffer, index type /// returns primitive topology, is_indexed, index_count, offset in index buffer, index type
std::tuple<VkPrimitiveTopology, u32, std::optional<std::tuple<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); void upload_vertex_buffers(u32 min_index, u32 vertex_max_index);
/// returns number of vertex drawn /// returns number of vertex drawn
u32 upload_inlined_array(); u32 upload_inlined_array();

View File

@ -233,6 +233,7 @@ VKGSRender::upload_vertex_data()
if (is_indexed_draw) if (is_indexed_draw)
{ {
std::tie(min_index, max_index, index_count, index_info) = upload_index_buffer(rsx::method_registers.current_draw_clause); std::tie(min_index, max_index, index_count, index_info) = upload_index_buffer(rsx::method_registers.current_draw_clause);
min_index = 0; // We need correct index mapping
} }
bool primitives_emulated = false; bool primitives_emulated = false;
@ -248,9 +249,8 @@ VKGSRender::upload_vertex_data()
{ {
index_count = rsx::method_registers.current_draw_clause.get_elements_count(); index_count = rsx::method_registers.current_draw_clause.get_elements_count();
} }
min_index = rsx::method_registers.current_draw_clause.first_count_commands.front().first;
min_index = 0; max_index = rsx::method_registers.current_draw_clause.get_elements_count() + min_index;
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) if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::inlined_array)
@ -265,127 +265,119 @@ VKGSRender::upload_vertex_data()
if (rsx::method_registers.current_draw_clause.command == rsx::draw_command::array || rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed) 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); upload_vertex_buffers(min_index, max_index);
} }
return std::make_tuple(prims, index_count, index_info); return std::make_tuple(prims, index_count, index_info);
} }
void VKGSRender::upload_vertex_buffers(u32 input_mask, u32 vertex_max_index) namespace
{ {
for (int index = 0; index < rsx::limits::vertex_count; ++index) struct vertex_buffer_visitor
{ {
bool enabled = !!(input_mask & (1 << index)); vertex_buffer_visitor(u32 vtx_cnt, VkDevice dev,
vk::vk_data_heap& heap, vk::glsl::program* prog,
if (!m_program->has_uniform(s_reg_table[index])) VkDescriptorSet desc_set,
continue; std::vector<std::unique_ptr<vk::buffer_view>>& buffer_view_to_clean)
: vertex_count(vtx_cnt)
if (!enabled) , m_attrib_ring_info(heap)
, device(dev)
, m_program(prog)
, descriptor_sets(desc_set)
, m_buffer_view_to_clean(buffer_view_to_clean)
{ {
continue;
} }
if (rsx::method_registers.vertex_arrays_info[index].size > 0) void operator()(const rsx::vertex_array_buffer& vertex_array)
{ {
auto &vertex_info = rsx::method_registers.vertex_arrays_info[index];
// Fill vertex_array // Fill vertex_array
u32 element_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size); u32 element_size = rsx::get_vertex_type_size_on_host(vertex_array.type, vertex_array.attribute_size);
u32 real_element_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size); u32 real_element_size = vk::get_suitable_vk_size(vertex_array.type, vertex_array.attribute_size);
u32 upload_size = real_element_size * (vertex_max_index + 1); u32 upload_size = real_element_size * vertex_count;
bool requires_expansion = vk::requires_component_expansion(vertex_info.type, vertex_info.size); bool requires_expansion = vk::requires_component_expansion(vertex_array.type, vertex_array.attribute_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); 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); 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); vk::prepare_buffer_for_writing(dst, vertex_array.type, vertex_array.attribute_size, vertex_count);
gsl::span<gsl::byte> dest_span(static_cast<gsl::byte*>(dst), upload_size); 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) write_vertex_array_data_to_buffer(dest_span, vertex_array.data, vertex_count, vertex_array.type, vertex_array.attribute_size, vertex_array.stride, real_element_size);
{
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(); m_attrib_ring_info.unmap();
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size); const VkFormat format = vk::get_suitable_vk_format(vertex_array.type, vertex_array.attribute_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_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(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); m_program->bind_uniform(m_buffer_view_to_clean.back()->value, s_reg_table[vertex_array.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) void operator()(const rsx::vertex_array_register& vertex_register)
{
switch (vertex_register.type)
{ {
case rsx::vertex_base_type::f: case rsx::vertex_base_type::f:
{ {
size_t data_size = rsx::get_vertex_type_size_on_host(vertex_info.type, vertex_info.size); size_t data_size = rsx::get_vertex_type_size_on_host(vertex_register.type, vertex_register.attribute_size);
const VkFormat format = vk::get_suitable_vk_format(vertex_info.type, vertex_info.size); const VkFormat format = vk::get_suitable_vk_format(vertex_register.type, vertex_register.attribute_size);
u32 offset_in_attrib_buffer = 0; u32 offset_in_attrib_buffer = 0;
void *data_ptr = vertex_info.data.data();
if (vk::requires_component_expansion(vertex_info.type, vertex_info.size)) if (vk::requires_component_expansion(vertex_register.type, vertex_register.attribute_size))
{ {
const u32 num_stored_verts = static_cast<u32>(data_size / (sizeof(float) * vertex_info.size)); const u32 num_stored_verts = static_cast<u32>(data_size / (sizeof(float) * vertex_register.attribute_size));
const u32 real_element_size = vk::get_suitable_vk_size(vertex_info.type, vertex_info.size); const u32 real_element_size = vk::get_suitable_vk_size(vertex_register.type, vertex_register.attribute_size);
data_size = real_element_size * num_stored_verts; data_size = real_element_size * num_stored_verts;
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(data_size); 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); 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); vk::expand_array_components<float, 3, 4, 1>(reinterpret_cast<const float*>(vertex_register.data.data()), dst, num_stored_verts);
m_attrib_ring_info.unmap(); m_attrib_ring_info.unmap();
} }
else else
{ {
offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(data_size); 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); void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, data_size);
memcpy(dst, vertex_info.data.data(), data_size); memcpy(dst, vertex_register.data.data(), data_size);
m_attrib_ring_info.unmap(); 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_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(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); m_program->bind_uniform(m_buffer_view_to_clean.back()->value, s_reg_table[vertex_register.index], descriptor_sets);
break; break;
} }
default: default:
fmt::throw_exception("Unknown base type %d" HERE, (u32)vertex_info.type); fmt::throw_exception("Unknown base type %d" HERE, (u32)vertex_register.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.
void operator()(const rsx::empty_vertex_array& vbo)
{
u32 offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(32); u32 offset_in_attrib_buffer = m_attrib_ring_info.alloc<256>(32);
void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, 32); void *dst = m_attrib_ring_info.map(offset_in_attrib_buffer, 32);
memset(dst, 0, 32); memset(dst, 0, 32);
m_attrib_ring_info.unmap(); m_attrib_ring_info.unmap();
m_buffer_view_to_clean.push_back(std::make_unique<vk::buffer_view>(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[vbo.index], descriptor_sets);
}
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)); protected:
m_program->bind_uniform(m_buffer_view_to_clean.back()->value, s_reg_table[index], descriptor_sets); VkDevice device;
} u32 vertex_count;
} vk::vk_data_heap& m_attrib_ring_info;
vk::glsl::program* m_program;
VkDescriptorSet descriptor_sets;
std::vector<std::unique_ptr<vk::buffer_view>>& m_buffer_view_to_clean;
};
} // End anonymous namespace
void VKGSRender::upload_vertex_buffers(u32 min_index, u32 vertex_max_index)
{
vertex_buffer_visitor visitor(vertex_max_index - min_index + 1, *m_device, m_attrib_ring_info, m_program, descriptor_sets, m_buffer_view_to_clean);
const auto& vertex_buffers = get_vertex_buffers(rsx::method_registers, {{min_index, vertex_max_index - min_index + 1}});
for (const auto& vbo : vertex_buffers)
std::apply_visitor(visitor, vbo);
} }
u32 VKGSRender::upload_inlined_array() u32 VKGSRender::upload_inlined_array()

View File

@ -213,8 +213,8 @@ SettingsDialog::SettingsDialog(wxWindow* parent)
std::vector<std::unique_ptr<cfg_adapter>> pads; std::vector<std::unique_ptr<cfg_adapter>> pads;
static const u32 width = 458; static const u32 width = 458 * 2;
static const u32 height = 400; static const u32 height = 400 * 2;
// Settings panels // Settings panels
wxNotebook* nb_config = new wxNotebook(this, wxID_ANY, wxPoint(6, 6), wxSize(width, height)); wxNotebook* nb_config = new wxNotebook(this, wxID_ANY, wxPoint(6, 6), wxSize(width, height));
@ -521,7 +521,7 @@ SettingsDialog::SettingsDialog(wxWindow* parent)
SetSizerAndFit(s_subpanel_system, false); SetSizerAndFit(s_subpanel_system, false);
SetSizerAndFit(s_b_panel, false); SetSizerAndFit(s_b_panel, false);
SetSize(width + 26, height + 80); SetSize(width + 26 * 2, height + 80 * 2);
if (ShowModal() == wxID_OK) if (ShowModal() == wxID_OK)
{ {