1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-25 20:22:30 +01:00

Get reworked RSX to compile

This commit is contained in:
kd-11 2024-04-10 04:02:12 +03:00 committed by kd-11
parent 10fe14e783
commit c1aaa1bcf6
31 changed files with 741 additions and 599 deletions

View File

@ -509,7 +509,7 @@ void GLGSRender::emit_geometry(u32 sub_index)
auto& draw_call = rsx::method_registers.current_draw_clause;
const rsx::flags32_t vertex_state_mask = rsx::vertex_base_changed | rsx::vertex_arrays_changed;
const rsx::flags32_t vertex_state = (sub_index == 0) ? rsx::vertex_arrays_changed : draw_call.execute_pipeline_dependencies() & vertex_state_mask;
const rsx::flags32_t vertex_state = (sub_index == 0) ? rsx::vertex_arrays_changed : draw_call.execute_pipeline_dependencies(m_ctx) & vertex_state_mask;
if (vertex_state & rsx::vertex_arrays_changed)
{
@ -540,7 +540,7 @@ void GLGSRender::emit_geometry(u32 sub_index)
// Execute remainining pipeline barriers with NOP draw
do
{
draw_call.execute_pipeline_dependencies();
draw_call.execute_pipeline_dependencies(m_ctx);
} while (draw_call.next());
draw_call.end();

View File

@ -0,0 +1,310 @@
#pragma once
#include "draw_call.inc.h"
#include "Emu/RSX/Common/simple_array.hpp"
#include "Emu/RSX/gcm_enums.h"
namespace rsx
{
class draw_clause
{
// Stores the first and count argument from draw/draw indexed parameters between begin/end clauses.
simple_array<draw_range_t> draw_command_ranges{};
// Stores rasterization barriers for primitive types sensitive to adjacency
simple_array<barrier_t> draw_command_barriers{};
// Counter used to parse the commands in order
u32 current_range_index{};
// Location of last execution barrier
u32 last_execution_barrier_index{};
// Helper functions
// Add a new draw command
void append_draw_command(const draw_range_t& range)
{
current_range_index = draw_command_ranges.size();
draw_command_ranges.push_back(range);
}
// Insert a new draw command within the others
void insert_draw_command(u32 index, const draw_range_t& range)
{
auto range_It = draw_command_ranges.begin();
std::advance(range_It, index);
current_range_index = index;
draw_command_ranges.insert(range_It, range);
// Update all barrier draw ids after this one
for (auto& barrier : draw_command_barriers)
{
if (barrier.draw_id >= index)
{
barrier.draw_id++;
}
}
}
public:
primitive_type primitive{};
draw_command command{};
bool is_immediate_draw{}; // Set if part of the draw is submitted via push registers
bool is_disjoint_primitive{}; // Set if primitive type does not rely on adjacency information
bool primitive_barrier_enable{}; // Set once to signal that a primitive restart barrier can be inserted
simple_array<u32> inline_vertex_array{};
void operator()(utils::serial& ar);
void insert_command_barrier(command_barrier_type type, u32 arg, u32 register_index = 0);
/**
* Optimize commands for rendering
*/
void compile()
{
// End draw call append mode
current_range_index = ~0u;
// TODO
}
/**
* Insert one command range
*/
void append(u32 first, u32 count)
{
const bool barrier_enable_flag = primitive_barrier_enable;
primitive_barrier_enable = false;
if (!draw_command_ranges.empty())
{
auto& last = draw_command_ranges[current_range_index];
if (last.count == 0)
{
// Special case, usually indicates an execution barrier
last.first = first;
last.count = count;
return;
}
if (last.first + last.count == first)
{
if (!is_disjoint_primitive && barrier_enable_flag)
{
// Insert barrier
insert_command_barrier(primitive_restart_barrier, 0);
}
last.count += count;
return;
}
for (auto index = last_execution_barrier_index; index < draw_command_ranges.size(); ++index)
{
if (draw_command_ranges[index].first == first &&
draw_command_ranges[index].count == count)
{
// Duplicate entry? WTF!
return;
}
if (draw_command_ranges[index].first > first)
{
insert_draw_command(index, { 0, first, count });
return;
}
}
}
append_draw_command({ 0, first, count });
}
/**
* Returns how many vertex or index will be consumed by the draw clause.
*/
u32 get_elements_count() const
{
if (draw_command_ranges.empty())
{
ensure(command == rsx::draw_command::inlined_array);
return 0;
}
return get_range().count;
}
u32 min_index() const
{
if (draw_command_ranges.empty())
{
ensure(command == rsx::draw_command::inlined_array);
return 0;
}
return get_range().first;
}
bool is_single_draw() const
{
if (is_disjoint_primitive)
return true;
if (draw_command_ranges.empty())
{
ensure(!inline_vertex_array.empty());
return true;
}
ensure(current_range_index != ~0u);
for (const auto& barrier : draw_command_barriers)
{
if (barrier.draw_id != current_range_index)
continue;
if (barrier.type == primitive_restart_barrier)
return false;
}
return true;
}
bool empty() const
{
return (command == rsx::draw_command::inlined_array) ? inline_vertex_array.empty() : draw_command_ranges.empty();
}
u32 pass_count() const
{
if (draw_command_ranges.empty())
{
ensure(!inline_vertex_array.empty());
return 1u;
}
u32 count = ::size32(draw_command_ranges);
if (draw_command_ranges.back().count == 0)
{
// Dangling barrier
ensure(count > 1);
count--;
}
return count;
}
primitive_class classify_mode() const
{
return primitive >= rsx::primitive_type::triangles
? primitive_class::polygon
: primitive_class::non_polygon;
}
void reset(rsx::primitive_type type);
void begin()
{
current_range_index = 0;
}
void end()
{
current_range_index = draw_command_ranges.size() - 1;
}
bool next()
{
current_range_index++;
if (current_range_index >= draw_command_ranges.size())
{
current_range_index = 0;
return false;
}
if (draw_command_ranges[current_range_index].count == 0)
{
// Dangling execution barrier
ensure(current_range_index > 0 && (current_range_index + 1) == draw_command_ranges.size());
current_range_index = 0;
return false;
}
return true;
}
/**
* Only call this once after the draw clause has been fully consumed to reconcile any conflicts
*/
void post_execute_cleanup(struct context* ctx)
{
ensure(current_range_index == 0);
if (draw_command_ranges.size() > 1)
{
if (draw_command_ranges.back().count == 0)
{
// Dangling execution barrier
current_range_index = draw_command_ranges.size() - 1;
execute_pipeline_dependencies(ctx);
current_range_index = 0;
}
}
}
/**
* Executes commands reqiured to make the current draw state valid
*/
u32 execute_pipeline_dependencies(struct context* ctx) const;
const draw_range_t& get_range() const
{
ensure(current_range_index < draw_command_ranges.size());
return draw_command_ranges[current_range_index];
}
simple_array<draw_range_t> get_subranges() const
{
ensure(!is_single_draw());
const auto range = get_range();
const auto limit = range.first + range.count;
simple_array<draw_range_t> ret;
u32 previous_barrier = range.first;
u32 vertex_counter = 0;
for (const auto& barrier : draw_command_barriers)
{
if (barrier.draw_id != current_range_index)
continue;
if (barrier.type != primitive_restart_barrier)
continue;
if (barrier.address <= range.first)
continue;
if (barrier.address >= limit)
break;
const u32 count = barrier.address - previous_barrier;
ret.push_back({ 0, vertex_counter, count });
previous_barrier = barrier.address;
vertex_counter += count;
}
ensure(!ret.empty());
ensure(previous_barrier < limit);
ret.push_back({ 0, vertex_counter, limit - previous_barrier });
return ret;
}
};
}

View File

@ -0,0 +1,68 @@
#pragma once
#include <util/types.hpp>
namespace rsx
{
enum class draw_command
{
none,
array,
inlined_array,
indexed,
};
enum command_barrier_type : u32
{
primitive_restart_barrier,
vertex_base_modifier_barrier,
index_base_modifier_barrier,
vertex_array_offset_modifier_barrier
};
enum command_execution_flags : u32
{
vertex_base_changed = (1 << 0),
index_base_changed = (1 << 1),
vertex_arrays_changed = (1 << 2),
};
enum class primitive_class
{
polygon,
non_polygon
};
struct barrier_t
{
u32 draw_id;
u64 timestamp;
u32 address;
u32 index;
u32 arg;
u32 flags;
command_barrier_type type;
bool operator < (const barrier_t& other) const
{
if (address != ~0u)
{
return address < other.address;
}
return timestamp < other.timestamp;
}
ENABLE_BITWISE_SERIALIZATION;
};
struct draw_range_t
{
u32 command_data_offset = 0;
u32 first = 0;
u32 count = 0;
ENABLE_BITWISE_SERIALIZATION;
};
}

View File

@ -0,0 +1 @@
#include "stdafx.h"

View File

@ -0,0 +1,12 @@
#pragma once
#include <util/types.hpp>
namespace rsx
{
// TODO: Basically replaces parts of the current "rsx_state" object
struct reg_context
{
u32 registers[1];
};
}

View File

@ -62,5 +62,25 @@ namespace rsx
return vm::cast(get_address(offset, location));
}
void set_fragment_texture_dirty_bit(rsx::context* ctx, u32 index)
{
RSX(ctx)->m_textures_dirty[index] = true;
if (RSX(ctx)->current_fp_metadata.referenced_textures_mask & (1 << index))
{
RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_program_state_dirty;
}
}
void set_vertex_texture_dirty_bit(rsx::context* ctx, u32 index)
{
RSX(ctx)->m_vertex_textures_dirty[index] = true;
if (RSX(ctx)->current_vp_metadata.referenced_textures_mask & (1 << index))
{
RSX(ctx)->m_graphics_state |= rsx::pipeline_state::vertex_program_state_dirty;
}
}
}
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <util/types.hpp>
#include "context.h"
#include "context_accessors.define.h"
namespace rsx
{
enum command_barrier_type : u32;
enum class vertex_base_type : u8;
namespace util
{
u32 get_report_data_impl(rsx::context* ctx, u32 offset);
void push_vertex_data(rsx::context* ctx, u32 attrib_index, u32 channel_select, int count, rsx::vertex_base_type vtype, u32 value);
void push_draw_parameter_change(rsx::context* ctx, rsx::command_barrier_type type, u32 reg, u32 arg);
void set_fragment_texture_dirty_bit(rsx::context* ctx, u32 index);
void set_vertex_texture_dirty_bit(rsx::context* ctx, u32 index);
}
}
#include "context_accessors.undef.h"

View File

@ -5,6 +5,7 @@
namespace rsx
{
class thread;
struct rsx_state;
#if 0
// TODO: Separate GRAPH context from RSX state

View File

@ -0,0 +1,140 @@
#include "stdafx.h"
#include "nv0039.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "context_accessors.define.h"
namespace rsx
{
namespace nv0039
{
void buffer_notify(context* ctx, u32, u32 arg)
{
s32 in_pitch = REGS(ctx)->nv0039_input_pitch();
s32 out_pitch = REGS(ctx)->nv0039_output_pitch();
const u32 line_length = REGS(ctx)->nv0039_line_length();
const u32 line_count = REGS(ctx)->nv0039_line_count();
const u8 out_format = REGS(ctx)->nv0039_output_format();
const u8 in_format = REGS(ctx)->nv0039_input_format();
const u32 notify = arg;
if (!line_count || !line_length)
{
rsx_log.warning("NV0039_BUFFER_NOTIFY NOPed out: pitch(in=0x%x, out=0x%x), line(len=0x%x, cnt=0x%x), fmt(in=0x%x, out=0x%x), notify=0x%x",
in_pitch, out_pitch, line_length, line_count, in_format, out_format, notify);
return;
}
rsx_log.trace("NV0039_BUFFER_NOTIFY: pitch(in=0x%x, out=0x%x), line(len=0x%x, cnt=0x%x), fmt(in=0x%x, out=0x%x), notify=0x%x",
in_pitch, out_pitch, line_length, line_count, in_format, out_format, notify);
u32 src_offset = REGS(ctx)->nv0039_input_offset();
u32 src_dma = REGS(ctx)->nv0039_input_location();
u32 dst_offset = REGS(ctx)->nv0039_output_offset();
u32 dst_dma = REGS(ctx)->nv0039_output_location();
const bool is_block_transfer = (in_pitch == out_pitch && out_pitch + 0u == line_length);
const auto read_address = get_address(src_offset, src_dma);
const auto write_address = get_address(dst_offset, dst_dma);
const auto read_length = in_pitch * (line_count - 1) + line_length;
const auto write_length = out_pitch * (line_count - 1) + line_length;
RSX(ctx)->invalidate_fragment_program(dst_dma, dst_offset, write_length);
if (const auto result = RSX(ctx)->read_barrier(read_address, read_length, !is_block_transfer);
result == rsx::result_zcull_intr)
{
// This transfer overlaps will zcull data pool
if (RSX(ctx)->copy_zcull_stats(read_address, read_length, write_address) == write_length)
{
// All writes deferred
return;
}
}
auto res = ::rsx::reservation_lock<true>(write_address, write_length, read_address, read_length);
u8 *dst = vm::_ptr<u8>(write_address);
const u8 *src = vm::_ptr<u8>(read_address);
const bool is_overlapping = dst_dma == src_dma && [&]() -> bool
{
const u32 src_max = src_offset + read_length;
const u32 dst_max = dst_offset + (out_pitch * (line_count - 1) + line_length);
return (src_offset >= dst_offset && src_offset < dst_max) ||
(dst_offset >= src_offset && dst_offset < src_max);
}();
if (in_format > 1 || out_format > 1) [[ unlikely ]]
{
// The formats are just input channel strides. You can use this to do cool tricks like gathering channels
// Very rare, only seen in use by Destiny
// TODO: Hw accel
for (u32 row = 0; row < line_count; ++row)
{
auto dst_ptr = dst;
auto src_ptr = src;
while (src_ptr < src + line_length)
{
*dst_ptr = *src_ptr;
src_ptr += in_format;
dst_ptr += out_format;
}
dst += out_pitch;
src += in_pitch;
}
}
else if (is_overlapping) [[ unlikely ]]
{
if (is_block_transfer)
{
std::memmove(dst, src, read_length);
}
else
{
std::vector<u8> temp(line_length * line_count);
u8* buf = temp.data();
for (u32 y = 0; y < line_count; ++y)
{
std::memcpy(buf, src, line_length);
buf += line_length;
src += in_pitch;
}
buf = temp.data();
for (u32 y = 0; y < line_count; ++y)
{
std::memcpy(dst, buf, line_length);
buf += line_length;
dst += out_pitch;
}
}
}
else
{
if (is_block_transfer)
{
std::memcpy(dst, src, read_length);
}
else
{
for (u32 i = 0; i < line_count; ++i)
{
std::memcpy(dst, src, line_length);
dst += out_pitch;
src += in_pitch;
}
}
}
//res->release(0);
}
}
}

View File

@ -0,0 +1,12 @@
// NV47 Buffer Management
#pragma once
#include "context.h"
namespace rsx
{
namespace nv0039
{
void buffer_notify(context* ctx, u32 reg, u32 arg);
}
}

View File

@ -2,6 +2,8 @@
#include "nv3089.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "Emu/RSX/Common/tiled_dma_copy.hpp"
#include "context_accessors.define.h"

View File

@ -1,4 +1,6 @@
// NV47 2D Blit Engine
#pragma once
#include "context.h"
namespace rsx

View File

@ -2,6 +2,7 @@
#include "nv308a.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "context_accessors.define.h"

View File

@ -1,3 +1,4 @@
// NV47 Format Conversion
#pragma once
#include "context.h"

View File

@ -1,6 +1,7 @@
#include "stdafx.h"
#include "nv406e.h"
#include "common.h"
#include "nv47_sync.hpp"
#include "Emu/RSX/RSXThread.h"

View File

@ -1,3 +1,4 @@
// NV47 Sync Objects
#pragma once
#include "context.h"

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "nv4097.h"
#include "nv47_sync.hpp"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Common/BufferUtils.h"
@ -10,13 +11,6 @@
namespace rsx
{
template<typename Type> struct vertex_data_type_from_element_type;
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::s32k; };
template<> struct vertex_data_type_from_element_type<s16> { static const vertex_base_type type = vertex_base_type::s1; };
namespace nv4097
{
///// Program management

View File

@ -3,13 +3,22 @@
#include "common.h"
#include "Emu/RSX/gcm_enums.h"
#include "Emu/RSX/NV47/FW/draw_call.inc.h"
namespace rsx
{
enum command_barrier_type;
enum vertex_base_type;
enum command_barrier_type : u32;
namespace nv4097
{
template<typename Type> struct vertex_data_type_from_element_type;
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::s32k; };
template<> struct vertex_data_type_from_element_type<s16> { static const vertex_base_type type = vertex_base_type::s1; };
void clear(context* ctx, u32 reg, u32 arg);
void clear_zcull(context* ctx, u32 reg, u32 arg);
@ -109,7 +118,7 @@ namespace rsx
arg = (be_data << 16) | (be_data >> 16);
}
util::push_vertex_data(attribute_index, vertex_subreg, count, vtype);
util::push_vertex_data(ctx, attribute_index, vertex_subreg, count, vtype, arg);
}
template<u32 index>
@ -207,14 +216,9 @@ namespace rsx
template<u32 index>
struct set_texture_dirty_bit
{
static void impl(context* ctx, u32 reg, u32 arg)
static void impl(context* ctx, u32 /*reg*/, u32 /*arg*/)
{
RSX(ctx)->m_textures_dirty[index] = true;
if (RSX(ctx)->current_fp_metadata.referenced_textures_mask & (1 << index))
{
RSX(ctx)->m_graphics_state |= rsx::pipeline_state::fragment_program_state_dirty;
}
util::set_fragment_texture_dirty_bit(ctx, index);
}
};
@ -223,12 +227,7 @@ namespace rsx
{
static void impl(context* ctx, u32 reg, u32 arg)
{
RSX(ctx)->m_vertex_textures_dirty[index] = true;
if (RSX(ctx)->current_vp_metadata.referenced_textures_mask & (1 << index))
{
RSX(ctx)->m_graphics_state |= rsx::pipeline_state::vertex_program_state_dirty;
}
}
};

View File

@ -0,0 +1,8 @@
// RSX Hardware definitions
#pragma once
#include "nv0039.h" // Buffer objects
#include "nv3089.h" // Blit engine (2D)
#include "nv308a.h" // Format conversion
#include "nv406e.h" // Sync objects
#include "nv4097.h" // 3D engine (GRAPH)

View File

@ -1,24 +1,16 @@
#pragma once
#include <util/types.hpp>
#include "context.h"
#include "Emu/RSX/RSXThread.h"
#include "context_accessors.define.h"
namespace rsx
{
enum command_barrier_type : u32;
enum vertex_base_type;
namespace util
{
u32 get_report_data_impl(rsx::context* ctx, u32 offset);
void push_vertex_data(rsx::context* ctx, u32 attrib_index, u32 channel_select, int count, rsx::vertex_base_type vtype, u32 value);
void push_draw_parameter_change(rsx::context* ctx, rsx::command_barrier_type type, u32 reg, u32 arg);
template <bool FlushDMA, bool FlushPipe>
void write_gcm_label(context* ctx, u32 address, u32 data)
static void write_gcm_label(context* ctx, u32 address, u32 data)
{
const bool is_flip_sema = (address == (RSX(ctx)->label_addr + 0x10) || address == (RSX(ctx)->device_addr + 0x30));
if (!is_flip_sema)

View File

@ -1,7 +0,0 @@
// 3D Engine definitions
#pragma once
#include "nv3089.h"
#include "nv308a.h"
#include "nv406e.h"
#include "nv4097.h"

View File

@ -7,7 +7,7 @@
#include "Core/RSXReservationLock.hpp"
#include "Emu/Memory/vm_reservation.h"
#include "Emu/Cell/lv2/sys_rsx.h"
#include "NV47/context.h"
#include "NV47/HW/context.h"
#include "util/asm.hpp"
@ -810,9 +810,6 @@ namespace rsx
}
}
// FIXME: This should be properly managed
rsx::context ctx{ .rsxthr = this, .register_state = &method_registers };
if (m_flattener.is_enabled()) [[unlikely]]
{
switch(m_flattener.test(command))
@ -825,14 +822,14 @@ namespace rsx
{
// Emit end command to close existing scope
AUDIT(in_begin_end);
methods[NV4097_SET_BEGIN_END](&ctx, NV4097_SET_BEGIN_END, 0);
methods[NV4097_SET_BEGIN_END](m_ctx, NV4097_SET_BEGIN_END, 0);
break;
}
case FIFO::EMIT_BARRIER:
{
AUDIT(in_begin_end);
methods[NV4097_SET_BEGIN_END](&ctx, NV4097_SET_BEGIN_END, 0);
methods[NV4097_SET_BEGIN_END](&ctx, NV4097_SET_BEGIN_END, m_flattener.get_primitive());
methods[NV4097_SET_BEGIN_END](m_ctx, NV4097_SET_BEGIN_END, 0);
methods[NV4097_SET_BEGIN_END](m_ctx, NV4097_SET_BEGIN_END, m_flattener.get_primitive());
break;
}
default:
@ -851,19 +848,19 @@ namespace rsx
const u32 reg = (command.reg & 0xffff) >> 2;
const u32 value = command.value;
ctx.register_state->decode(reg, value);
m_ctx->register_state->decode(reg, value);
if (auto method = methods[reg])
{
method(&ctx, reg, value);
method(m_ctx, reg, value);
if (state & cpu_flag::again)
{
ctx.register_state->decode(reg, ctx.register_state->latch);
m_ctx->register_state->decode(reg, m_ctx->register_state->latch);
break;
}
}
else if (ctx.register_state->latch != value)
else if (m_ctx->register_state->latch != value)
{
// Something changed, set signal flags if any specified
m_graphics_state |= state_signals[reg];

View File

@ -27,6 +27,7 @@
#include "Utilities/date_time.h"
#include "Utilities/StrUtil.h"
#include "Crypto/unzip.h"
#include "NV47/HW/context.h"
#include "util/asm.hpp"
@ -122,6 +123,9 @@ namespace rsx
{
std::function<bool(u32 addr, bool is_writing)> g_access_violation_handler;
// TODO: Proper context manager
static rsx::context s_ctx{ .rsxthr = nullptr, .register_state = &method_registers };
rsx_iomap_table::rsx_iomap_table() noexcept
: ea(fill_array(-1))
, io(fill_array(-1))
@ -675,6 +679,10 @@ namespace rsx
g_user_asked_for_frame_capture = false;
// TODO: Proper context management in the driver
s_ctx.rsxthr = this;
m_ctx = &s_ctx;
if (g_cfg.misc.use_native_interface && (g_cfg.video.renderer == video_renderer::opengl || g_cfg.video.renderer == video_renderer::vulkan))
{
m_overlay_manager = g_fxo->init<rsx::overlays::display_manager>(0);
@ -813,7 +821,7 @@ namespace rsx
in_begin_end = false;
m_frame_stats.draw_calls++;
method_registers.current_draw_clause.post_execute_cleanup();
method_registers.current_draw_clause.post_execute_cleanup(m_ctx);
m_graphics_state |= rsx::pipeline_state::framebuffer_reads_dirty;
m_eng_interrupt_mask |= rsx::backend_interrupt;
@ -848,7 +856,7 @@ namespace rsx
method_registers.current_draw_clause.begin();
do
{
method_registers.current_draw_clause.execute_pipeline_dependencies();
method_registers.current_draw_clause.execute_pipeline_dependencies(m_ctx);
}
while (method_registers.current_draw_clause.next());
}
@ -918,7 +926,7 @@ namespace rsx
namespace nv4097
{
void set_render_mode(thread* rsx, u32, u32 arg);
void set_render_mode(context* rsx, u32, u32 arg);
}
void thread::on_task()
@ -958,7 +966,7 @@ namespace rsx
}
check_zcull_status(false);
nv4097::set_render_mode(this, 0, method_registers.registers[NV4097_SET_RENDER_ENABLE]);
nv4097::set_render_mode(m_ctx, 0, method_registers.registers[NV4097_SET_RENDER_ENABLE]);
performance_counters.state = FIFO::state::empty;
@ -2674,7 +2682,7 @@ namespace rsx
{
rsx::method_registers.reset();
check_zcull_status(false);
nv4097::set_render_mode(this, 0, method_registers.registers[NV4097_SET_RENDER_ENABLE]);
nv4097::set_render_mode(m_ctx, 0, method_registers.registers[NV4097_SET_RENDER_ENABLE]);
m_graphics_state |= pipeline_state::all_dirty;
}

View File

@ -40,6 +40,8 @@ extern rsx::frame_capture_data frame_capture;
namespace rsx
{
struct context;
namespace overlays
{
class display_manager;
@ -204,6 +206,9 @@ namespace rsx
// Savestates related
u32 m_pause_after_x_flips = 0;
// Context
context* m_ctx = nullptr;
public:
atomic_t<u64> new_get_put = u64{umax};
u32 restore_point = 0;

View File

@ -707,7 +707,7 @@ void VKGSRender::emit_geometry(u32 sub_index)
m_profiler.start();
const rsx::flags32_t vertex_state_mask = rsx::vertex_base_changed | rsx::vertex_arrays_changed;
const rsx::flags32_t vertex_state = (sub_index == 0) ? rsx::vertex_arrays_changed : draw_call.execute_pipeline_dependencies() & vertex_state_mask;
const rsx::flags32_t vertex_state = (sub_index == 0) ? rsx::vertex_arrays_changed : draw_call.execute_pipeline_dependencies(m_ctx) & vertex_state_mask;
if (vertex_state & rsx::vertex_arrays_changed)
{
@ -738,7 +738,7 @@ void VKGSRender::emit_geometry(u32 sub_index)
// Execute remainining pipeline barriers with NOP draw
do
{
draw_call.execute_pipeline_dependencies();
draw_call.execute_pipeline_dependencies(m_ctx);
}
while (draw_call.next());

View File

@ -4,14 +4,13 @@
#include "rsx_utils.h"
#include "rsx_decode.h"
#include "Common/time.hpp"
#include "Common/tiled_dma_copy.hpp"
#include "Core/RSXReservationLock.hpp"
#include "Emu/Cell/PPUCallback.h"
#include "Emu/Cell/lv2/sys_rsx.h"
#include "Emu/RSX/Common/BufferUtils.h"
#include "Emu/RSX/NV47/nv47.h"
#include "Emu/RSX/NV47/context_accessors.define.h"
#include "Emu/RSX/NV47/HW/nv47.h"
#include "Emu/RSX/NV47/HW/nv47_sync.hpp"
#include "Emu/RSX/NV47/HW/context_accessors.define.h" // TODO: Context objects belong in FW not HW
namespace rsx
{
@ -42,132 +41,7 @@ namespace rsx
namespace nv0039
{
void buffer_notify(context* ctx, u32, u32 arg)
{
s32 in_pitch = REGS(ctx)->nv0039_input_pitch();
s32 out_pitch = REGS(ctx)->nv0039_output_pitch();
const u32 line_length = REGS(ctx)->nv0039_line_length();
const u32 line_count = REGS(ctx)->nv0039_line_count();
const u8 out_format = REGS(ctx)->nv0039_output_format();
const u8 in_format = REGS(ctx)->nv0039_input_format();
const u32 notify = arg;
if (!line_count || !line_length)
{
rsx_log.warning("NV0039_BUFFER_NOTIFY NOPed out: pitch(in=0x%x, out=0x%x), line(len=0x%x, cnt=0x%x), fmt(in=0x%x, out=0x%x), notify=0x%x",
in_pitch, out_pitch, line_length, line_count, in_format, out_format, notify);
return;
}
rsx_log.trace("NV0039_BUFFER_NOTIFY: pitch(in=0x%x, out=0x%x), line(len=0x%x, cnt=0x%x), fmt(in=0x%x, out=0x%x), notify=0x%x",
in_pitch, out_pitch, line_length, line_count, in_format, out_format, notify);
u32 src_offset = REGS(ctx)->nv0039_input_offset();
u32 src_dma = REGS(ctx)->nv0039_input_location();
u32 dst_offset = REGS(ctx)->nv0039_output_offset();
u32 dst_dma = REGS(ctx)->nv0039_output_location();
const bool is_block_transfer = (in_pitch == out_pitch && out_pitch + 0u == line_length);
const auto read_address = get_address(src_offset, src_dma);
const auto write_address = get_address(dst_offset, dst_dma);
const auto read_length = in_pitch * (line_count - 1) + line_length;
const auto write_length = out_pitch * (line_count - 1) + line_length;
RSX(ctx)->invalidate_fragment_program(dst_dma, dst_offset, write_length);
if (const auto result = RSX(ctx)->read_barrier(read_address, read_length, !is_block_transfer);
result == rsx::result_zcull_intr)
{
// This transfer overlaps will zcull data pool
if (RSX(ctx)->copy_zcull_stats(read_address, read_length, write_address) == write_length)
{
// All writes deferred
return;
}
}
auto res = ::rsx::reservation_lock<true>(write_address, write_length, read_address, read_length);
u8 *dst = vm::_ptr<u8>(write_address);
const u8 *src = vm::_ptr<u8>(read_address);
const bool is_overlapping = dst_dma == src_dma && [&]() -> bool
{
const u32 src_max = src_offset + read_length;
const u32 dst_max = dst_offset + (out_pitch * (line_count - 1) + line_length);
return (src_offset >= dst_offset && src_offset < dst_max) ||
(dst_offset >= src_offset && dst_offset < src_max);
}();
if (in_format > 1 || out_format > 1) [[ unlikely ]]
{
// The formats are just input channel strides. You can use this to do cool tricks like gathering channels
// Very rare, only seen in use by Destiny
// TODO: Hw accel
for (u32 row = 0; row < line_count; ++row)
{
auto dst_ptr = dst;
auto src_ptr = src;
while (src_ptr < src + line_length)
{
*dst_ptr = *src_ptr;
src_ptr += in_format;
dst_ptr += out_format;
}
dst += out_pitch;
src += in_pitch;
}
}
else if (is_overlapping) [[ unlikely ]]
{
if (is_block_transfer)
{
std::memmove(dst, src, read_length);
}
else
{
std::vector<u8> temp(line_length * line_count);
u8* buf = temp.data();
for (u32 y = 0; y < line_count; ++y)
{
std::memcpy(buf, src, line_length);
buf += line_length;
src += in_pitch;
}
buf = temp.data();
for (u32 y = 0; y < line_count; ++y)
{
std::memcpy(dst, buf, line_length);
buf += line_length;
dst += out_pitch;
}
}
}
else
{
if (is_block_transfer)
{
std::memcpy(dst, src, read_length);
}
else
{
for (u32 i = 0; i < line_count; ++i)
{
std::memcpy(dst, src, line_length);
dst += out_pitch;
src += in_pitch;
}
}
}
//res->release(0);
}
}
void flip_command(context* ctx, u32, u32 arg)
@ -1414,7 +1288,7 @@ namespace rsx
is_disjoint_primitive = is_primitive_disjointed(primitive);
}
u32 draw_clause::execute_pipeline_dependencies() const
u32 draw_clause::execute_pipeline_dependencies(context* ctx) const
{
u32 result = 0;
@ -1974,7 +1848,8 @@ namespace rsx
// FIFO
bind(FIFO::FIFO_DRAW_BARRIER >> 2, fifo::draw_barrier);
REGS(ctx)->init();
// REGS(ctx)->init();
method_registers.init();
return true;
}();

View File

@ -10,372 +10,11 @@
#include "Emu/Cell/timers.hpp"
#include "Program/program_util.h"
#include "NV47/FW/draw_call.hpp"
namespace rsx
{
enum class draw_command
{
none,
array,
inlined_array,
indexed,
};
enum command_barrier_type : u32
{
primitive_restart_barrier,
vertex_base_modifier_barrier,
index_base_modifier_barrier,
vertex_array_offset_modifier_barrier
};
enum command_execution_flags : u32
{
vertex_base_changed = (1 << 0),
index_base_changed = (1 << 1),
vertex_arrays_changed = (1 << 2),
};
enum class primitive_class
{
polygon,
non_polygon
};
struct barrier_t
{
u32 draw_id;
u64 timestamp;
u32 address;
u32 index;
u32 arg;
u32 flags;
command_barrier_type type;
bool operator < (const barrier_t& other) const
{
if (address != ~0u)
{
return address < other.address;
}
return timestamp < other.timestamp;
}
ENABLE_BITWISE_SERIALIZATION;
};
struct draw_range_t
{
u32 command_data_offset = 0;
u32 first = 0;
u32 count = 0;
ENABLE_BITWISE_SERIALIZATION;
};
class draw_clause
{
// Stores the first and count argument from draw/draw indexed parameters between begin/end clauses.
simple_array<draw_range_t> draw_command_ranges{};
// Stores rasterization barriers for primitive types sensitive to adjacency
simple_array<barrier_t> draw_command_barriers{};
// Counter used to parse the commands in order
u32 current_range_index{};
// Location of last execution barrier
u32 last_execution_barrier_index{};
// Helper functions
// Add a new draw command
void append_draw_command(const draw_range_t& range)
{
current_range_index = draw_command_ranges.size();
draw_command_ranges.push_back(range);
}
// Insert a new draw command within the others
void insert_draw_command(u32 index, const draw_range_t& range)
{
auto range_It = draw_command_ranges.begin();
std::advance(range_It, index);
current_range_index = index;
draw_command_ranges.insert(range_It, range);
// Update all barrier draw ids after this one
for (auto &barrier : draw_command_barriers)
{
if (barrier.draw_id >= index)
{
barrier.draw_id++;
}
}
}
public:
primitive_type primitive{};
draw_command command{};
bool is_immediate_draw{}; // Set if part of the draw is submitted via push registers
bool is_disjoint_primitive{}; // Set if primitive type does not rely on adjacency information
bool primitive_barrier_enable{}; // Set once to signal that a primitive restart barrier can be inserted
simple_array<u32> inline_vertex_array{};
void operator()(utils::serial& ar);
void insert_command_barrier(command_barrier_type type, u32 arg, u32 register_index = 0);
/**
* Optimize commands for rendering
*/
void compile()
{
// End draw call append mode
current_range_index = ~0u;
// TODO
}
/**
* Insert one command range
*/
void append(u32 first, u32 count)
{
const bool barrier_enable_flag = primitive_barrier_enable;
primitive_barrier_enable = false;
if (!draw_command_ranges.empty())
{
auto& last = draw_command_ranges[current_range_index];
if (last.count == 0)
{
// Special case, usually indicates an execution barrier
last.first = first;
last.count = count;
return;
}
if (last.first + last.count == first)
{
if (!is_disjoint_primitive && barrier_enable_flag)
{
// Insert barrier
insert_command_barrier(primitive_restart_barrier, 0);
}
last.count += count;
return;
}
for (auto index = last_execution_barrier_index; index < draw_command_ranges.size(); ++index)
{
if (draw_command_ranges[index].first == first &&
draw_command_ranges[index].count == count)
{
// Duplicate entry? WTF!
return;
}
if (draw_command_ranges[index].first > first)
{
insert_draw_command(index, { 0, first, count });
return;
}
}
}
append_draw_command({ 0, first, count });
}
/**
* Returns how many vertex or index will be consumed by the draw clause.
*/
u32 get_elements_count() const
{
if (draw_command_ranges.empty())
{
ensure(command == rsx::draw_command::inlined_array);
return 0;
}
return get_range().count;
}
u32 min_index() const
{
if (draw_command_ranges.empty())
{
ensure(command == rsx::draw_command::inlined_array);
return 0;
}
return get_range().first;
}
bool is_single_draw() const
{
if (is_disjoint_primitive)
return true;
if (draw_command_ranges.empty())
{
ensure(!inline_vertex_array.empty());
return true;
}
ensure(current_range_index != ~0u);
for (const auto &barrier : draw_command_barriers)
{
if (barrier.draw_id != current_range_index)
continue;
if (barrier.type == primitive_restart_barrier)
return false;
}
return true;
}
bool empty() const
{
return (command == rsx::draw_command::inlined_array) ? inline_vertex_array.empty() : draw_command_ranges.empty();
}
u32 pass_count() const
{
if (draw_command_ranges.empty())
{
ensure(!inline_vertex_array.empty());
return 1u;
}
u32 count = ::size32(draw_command_ranges);
if (draw_command_ranges.back().count == 0)
{
// Dangling barrier
ensure(count > 1);
count--;
}
return count;
}
primitive_class classify_mode() const
{
return primitive >= rsx::primitive_type::triangles
? primitive_class::polygon
: primitive_class::non_polygon;
}
void reset(rsx::primitive_type type);
void begin()
{
current_range_index = 0;
}
void end()
{
current_range_index = draw_command_ranges.size() - 1;
}
bool next()
{
current_range_index++;
if (current_range_index >= draw_command_ranges.size())
{
current_range_index = 0;
return false;
}
if (draw_command_ranges[current_range_index].count == 0)
{
// Dangling execution barrier
ensure(current_range_index > 0 && (current_range_index + 1) == draw_command_ranges.size());
current_range_index = 0;
return false;
}
return true;
}
/**
* Only call this once after the draw clause has been fully consumed to reconcile any conflicts
*/
void post_execute_cleanup()
{
ensure(current_range_index == 0);
if (draw_command_ranges.size() > 1)
{
if (draw_command_ranges.back().count == 0)
{
// Dangling execution barrier
current_range_index = draw_command_ranges.size() - 1;
execute_pipeline_dependencies();
current_range_index = 0;
}
}
}
/**
* Executes commands reqiured to make the current draw state valid
*/
u32 execute_pipeline_dependencies() const;
const draw_range_t& get_range() const
{
ensure(current_range_index < draw_command_ranges.size());
return draw_command_ranges[current_range_index];
}
simple_array<draw_range_t> get_subranges() const
{
ensure(!is_single_draw());
const auto range = get_range();
const auto limit = range.first + range.count;
simple_array<draw_range_t> ret;
u32 previous_barrier = range.first;
u32 vertex_counter = 0;
for (const auto &barrier : draw_command_barriers)
{
if (barrier.draw_id != current_range_index)
continue;
if (barrier.type != primitive_restart_barrier)
continue;
if (barrier.address <= range.first)
continue;
if (barrier.address >= limit)
break;
const u32 count = barrier.address - previous_barrier;
ret.push_back({ 0, vertex_counter, count });
previous_barrier = barrier.address;
vertex_counter += count;
}
ensure(!ret.empty());
ensure(previous_barrier < limit);
ret.push_back({ 0, vertex_counter, limit - previous_barrier });
return ret;
}
};
using rsx_method_t = void(*)(class context*, u32 reg, u32 arg);
using rsx_method_t = void(*)(struct context*, u32 reg, u32 arg);
//TODO
union alignas(4) method_registers_t

View File

@ -95,11 +95,13 @@
<ClCompile Include="Emu\perf_monitor.cpp" />
<ClCompile Include="Emu\RSX\Common\texture_cache.cpp" />
<ClCompile Include="Emu\RSX\Core\RSXContext.cpp" />
<ClCompile Include="Emu\RSX\NV47\common.cpp" />
<ClCompile Include="Emu\RSX\NV47\nv3089.cpp" />
<ClCompile Include="Emu\RSX\NV47\nv308a.cpp" />
<ClCompile Include="Emu\RSX\NV47\nv406e.cpp" />
<ClCompile Include="Emu\RSX\NV47\nv4097.cpp" />
<ClCompile Include="Emu\RSX\NV47\FW\reg_context.cpp" />
<ClCompile Include="Emu\RSX\NV47\HW\common.cpp" />
<ClCompile Include="Emu\RSX\NV47\HW\nv0039.cpp" />
<ClCompile Include="Emu\RSX\NV47\HW\nv3089.cpp" />
<ClCompile Include="Emu\RSX\NV47\HW\nv308a.cpp" />
<ClCompile Include="Emu\RSX\NV47\HW\nv406e.cpp" />
<ClCompile Include="Emu\RSX\NV47\HW\nv4097.cpp" />
<ClCompile Include="Emu\RSX\Overlays\HomeMenu\overlay_home_menu.cpp" />
<ClCompile Include="Emu\RSX\Overlays\HomeMenu\overlay_home_menu_components.cpp" />
<ClCompile Include="Emu\RSX\Overlays\HomeMenu\overlay_home_menu_message_box.cpp" />
@ -589,15 +591,20 @@
<ClInclude Include="Emu\RSX\Core\RSXDisplay.h" />
<ClInclude Include="Emu\RSX\Core\RSXReservationLock.hpp" />
<ClInclude Include="Emu\RSX\Core\RSXVertexTypes.h" />
<ClInclude Include="Emu\RSX\NV47\context.h" />
<ClInclude Include="Emu\RSX\NV47\context_accessors.define.h" />
<ClInclude Include="Emu\RSX\NV47\context_accessors.undef.h" />
<ClInclude Include="Emu\RSX\NV47\nv3089.h" />
<ClInclude Include="Emu\RSX\NV47\nv308a.h" />
<ClInclude Include="Emu\RSX\NV47\nv406e.h" />
<ClInclude Include="Emu\RSX\NV47\nv4097.h" />
<ClInclude Include="Emu\RSX\NV47\nv47.h" />
<ClInclude Include="Emu\RSX\NV47\common.h" />
<ClInclude Include="Emu\RSX\NV47\FW\draw_call.hpp" />
<ClInclude Include="Emu\RSX\NV47\FW\draw_call.inc.h" />
<ClInclude Include="Emu\RSX\NV47\FW\reg_context.h" />
<ClInclude Include="Emu\RSX\NV47\HW\context.h" />
<ClInclude Include="Emu\RSX\NV47\HW\context_accessors.define.h" />
<ClInclude Include="Emu\RSX\NV47\HW\context_accessors.undef.h" />
<ClInclude Include="Emu\RSX\NV47\HW\nv0039.h" />
<ClInclude Include="Emu\RSX\NV47\HW\nv3089.h" />
<ClInclude Include="Emu\RSX\NV47\HW\nv308a.h" />
<ClInclude Include="Emu\RSX\NV47\HW\nv406e.h" />
<ClInclude Include="Emu\RSX\NV47\HW\nv4097.h" />
<ClInclude Include="Emu\RSX\NV47\HW\nv47.h" />
<ClInclude Include="Emu\RSX\NV47\HW\common.h" />
<ClInclude Include="Emu\RSX\NV47\HW\nv47_sync.hpp" />
<ClInclude Include="Emu\RSX\Overlays\HomeMenu\overlay_home_menu.h" />
<ClInclude Include="Emu\RSX\Overlays\HomeMenu\overlay_home_menu_components.h" />
<ClInclude Include="Emu\RSX\Overlays\HomeMenu\overlay_home_menu_message_box.h" />

View File

@ -100,6 +100,12 @@
<Filter Include="Emu\GPU\RSX\NV47">
<UniqueIdentifier>{213387bd-09c5-4247-8fb0-b3cae06ba34b}</UniqueIdentifier>
</Filter>
<Filter Include="Emu\GPU\RSX\NV47\HW">
<UniqueIdentifier>{24fc03a5-0469-4b6e-89df-44459e51855f}</UniqueIdentifier>
</Filter>
<Filter Include="Emu\GPU\RSX\NV47\FW">
<UniqueIdentifier>{fdb8ff8f-5a03-45a2-8c0a-16a89b7a574b}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Crypto\aes.cpp">
@ -1213,20 +1219,26 @@
<ClCompile Include="Emu\RSX\Program\SPIRVCommon.cpp">
<Filter>Emu\GPU\RSX\Program</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\NV47\nv4097.cpp">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClCompile Include="Emu\RSX\NV47\HW\common.cpp">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\NV47\common.cpp">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClCompile Include="Emu\RSX\NV47\HW\nv0039.cpp">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\NV47\nv406e.cpp">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClCompile Include="Emu\RSX\NV47\HW\nv308a.cpp">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\NV47\nv308a.cpp">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClCompile Include="Emu\RSX\NV47\HW\nv406e.cpp">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\NV47\nv3089.cpp">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClCompile Include="Emu\RSX\NV47\HW\nv3089.cpp">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\NV47\HW\nv4097.cpp">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\NV47\FW\reg_context.cpp">
<Filter>Emu\GPU\RSX\NV47\FW</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
@ -2467,32 +2479,47 @@
<ClInclude Include="Emu\RSX\Program\SPIRVCommon.h">
<Filter>Emu\GPU\RSX\Program</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\nv47.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\common.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\nv4097.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\context.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\nv406e.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\context_accessors.define.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\nv3089.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\context_accessors.undef.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\nv308a.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\nv0039.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\context.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\nv47.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\common.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\nv47_sync.hpp">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\context_accessors.define.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\nv308a.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\context_accessors.undef.h">
<Filter>Emu\GPU\RSX\NV47</Filter>
<ClInclude Include="Emu\RSX\NV47\HW\nv406e.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\HW\nv3089.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\HW\nv4097.h">
<Filter>Emu\GPU\RSX\NV47\HW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\FW\draw_call.inc.h">
<Filter>Emu\GPU\RSX\NV47\FW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\FW\reg_context.h">
<Filter>Emu\GPU\RSX\NV47\FW</Filter>
</ClInclude>
<ClInclude Include="Emu\RSX\NV47\FW\draw_call.hpp">
<Filter>Emu\GPU\RSX\NV47\FW</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>