[GPU] Primitive processor with Vulkan 1.0 base cases, 24-bit indices and clamping

This commit is contained in:
Triang3l
2021-06-04 23:56:25 +03:00
parent 10ff77a24f
commit 538aa91015
52 changed files with 5418 additions and 3638 deletions

View File

@@ -1177,40 +1177,77 @@ bool CommandProcessor::ExecutePacketType3_EVENT_WRITE_ZPD(RingBuffer* reader,
return true;
}
bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// initiate fetch of index buffer and draw
// if dword0 != 0, this is a conditional draw based on viz query.
bool CommandProcessor::ExecutePacketType3Draw(RingBuffer* reader,
uint32_t packet,
const char* opcode_name,
uint32_t viz_query_condition,
uint32_t count_remaining) {
// if viz_query_condition != 0, this is a conditional draw based on viz query.
// This ID matches the one issued in PM4_VIZ_QUERY
uint32_t dword0 = reader->ReadAndSwap<uint32_t>(); // viz query info
// uint32_t viz_id = dword0 & 0x3F;
// uint32_t viz_id = viz_query_condition & 0x3F;
// when true, render conditionally based on query result
// uint32_t viz_use = dword0 & 0x100;
// uint32_t viz_use = viz_query_condition & 0x100;
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("{}: Packet too small, can't read VGT_DRAW_INITIATOR", opcode_name);
return false;
}
reg::VGT_DRAW_INITIATOR vgt_draw_initiator;
vgt_draw_initiator.value = reader->ReadAndSwap<uint32_t>();
--count_remaining;
WriteRegister(XE_GPU_REG_VGT_DRAW_INITIATOR, vgt_draw_initiator.value);
bool success = true;
// TODO(Triang3l): Remove IndexBufferInfo and replace handling of all this
// with PrimitiveProcessor when the old Vulkan renderer is removed.
bool is_indexed = false;
IndexBufferInfo index_buffer_info;
switch (vgt_draw_initiator.source_select) {
case xenos::SourceSelect::kDMA: {
// Indexed draw.
is_indexed = true;
index_buffer_info.guest_base = reader->ReadAndSwap<uint32_t>();
uint32_t index_size = reader->ReadAndSwap<uint32_t>();
index_buffer_info.endianness =
static_cast<xenos::Endian>(index_size >> 30);
index_size &= 0x00FFFFFF;
// Two separate bounds checks so if there's only one missing register
// value out of two, one uint32_t will be skipped in the command buffer,
// not two.
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("{}: Packet too small, can't read VGT_DMA_BASE", opcode_name);
return false;
}
uint32_t vgt_dma_base = reader->ReadAndSwap<uint32_t>();
--count_remaining;
WriteRegister(XE_GPU_REG_VGT_DMA_BASE, vgt_dma_base);
reg::VGT_DMA_SIZE vgt_dma_size;
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("{}: Packet too small, can't read VGT_DMA_SIZE", opcode_name);
return false;
}
vgt_dma_size.value = reader->ReadAndSwap<uint32_t>();
--count_remaining;
WriteRegister(XE_GPU_REG_VGT_DMA_SIZE, vgt_dma_size.value);
uint32_t index_size_bytes =
vgt_draw_initiator.index_size == xenos::IndexFormat::kInt16
? sizeof(uint16_t)
: sizeof(uint32_t);
// The base address must already be word-aligned according to the R6xx
// documentation, but for safety.
index_buffer_info.guest_base = vgt_dma_base & ~(index_size_bytes - 1);
index_buffer_info.endianness = vgt_dma_size.swap_mode;
index_buffer_info.format = vgt_draw_initiator.index_size;
index_size *=
(vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32) ? 4 : 2;
index_buffer_info.length = index_size;
index_buffer_info.length = vgt_dma_size.num_words * index_size_bytes;
index_buffer_info.count = vgt_draw_initiator.num_indices;
} break;
case xenos::SourceSelect::kImmediate: {
// TODO(Triang3l): VGT_IMMED_DATA.
XELOGE(
"{}: Using immediate vertex indices, which are not supported yet. "
"Report the game to Xenia developers!",
opcode_name, uint32_t(vgt_draw_initiator.source_select));
success = false;
assert_always();
} break;
case xenos::SourceSelect::kAutoIndex: {
@@ -1219,71 +1256,65 @@ bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
index_buffer_info.length = 0;
} break;
default: {
// Invalid source select.
assert_always();
// Invalid source selection.
success = false;
assert_unhandled_case(vgt_draw_initiator.source_select);
} break;
}
auto viz_query = register_file_->Get<reg::PA_SC_VIZ_QUERY>();
if (viz_query.viz_query_ena && viz_query.kill_pix_post_hi_z) {
// TODO(Triang3l): Don't drop the draw call completely if the vertex shader
// has memexport.
// TODO(Triang3l || JoelLinn): Handle this properly in the render backends.
return true;
// Skip to the next command, for example, if there are immediate indexes that
// we don't support yet.
reader->AdvanceRead(count_remaining * sizeof(uint32_t));
if (success) {
auto viz_query = register_file_->Get<reg::PA_SC_VIZ_QUERY>();
if (!(viz_query.viz_query_ena && viz_query.kill_pix_post_hi_z)) {
// TODO(Triang3l): Don't drop the draw call completely if the vertex
// shader has memexport.
// TODO(Triang3l || JoelLinn): Handle this properly in the render
// backends.
success = IssueDraw(
vgt_draw_initiator.prim_type, vgt_draw_initiator.num_indices,
is_indexed ? &index_buffer_info : nullptr,
xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type));
if (!success) {
XELOGE("{}({}, {}, {}): Failed in backend", opcode_name,
vgt_draw_initiator.num_indices,
uint32_t(vgt_draw_initiator.prim_type),
uint32_t(vgt_draw_initiator.source_select));
}
}
}
bool success =
IssueDraw(vgt_draw_initiator.prim_type, vgt_draw_initiator.num_indices,
is_indexed ? &index_buffer_info : nullptr,
xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type));
if (!success) {
XELOGE("PM4_DRAW_INDX({}, {}, {}): Failed in backend",
vgt_draw_initiator.num_indices,
uint32_t(vgt_draw_initiator.prim_type),
uint32_t(vgt_draw_initiator.source_select));
}
return success;
}
return true;
bool CommandProcessor::ExecutePacketType3_DRAW_INDX(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// "initiate fetch of index buffer and draw"
// Generally used by Xbox 360 Direct3D 9 for kDMA and kAutoIndex sources.
// With a viz query token as the first one.
uint32_t count_remaining = count;
assert_not_zero(count_remaining);
if (!count_remaining) {
XELOGE("PM4_DRAW_INDX: Packet too small, can't read the viz query token");
return false;
}
uint32_t viz_query_condition = reader->ReadAndSwap<uint32_t>();
--count_remaining;
return ExecutePacketType3Draw(reader, packet, "PM4_DRAW_INDX",
viz_query_condition, count_remaining);
}
bool CommandProcessor::ExecutePacketType3_DRAW_INDX_2(RingBuffer* reader,
uint32_t packet,
uint32_t count) {
// draw using supplied indices in packet
reg::VGT_DRAW_INITIATOR vgt_draw_initiator;
vgt_draw_initiator.value = reader->ReadAndSwap<uint32_t>();
WriteRegister(XE_GPU_REG_VGT_DRAW_INITIATOR, vgt_draw_initiator.value);
assert_true(vgt_draw_initiator.source_select ==
xenos::SourceSelect::kAutoIndex);
// Index buffer unused as automatic.
// uint32_t indices_size =
// vgt_draw_initiator.num_indices *
// (vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32 ? 4
// : 2);
// uint32_t index_ptr = reader->ptr();
// TODO(Triang3l): VGT_IMMED_DATA.
reader->AdvanceRead((count - 1) * sizeof(uint32_t));
auto viz_query = register_file_->Get<reg::PA_SC_VIZ_QUERY>();
if (viz_query.viz_query_ena && viz_query.kill_pix_post_hi_z) {
// TODO(Triang3l): Don't drop the draw call completely if the vertex shader
// has memexport.
// TODO(Triang3l || JoelLinn): Handle this properly in the render backends.
return true;
}
bool success = IssueDraw(
vgt_draw_initiator.prim_type, vgt_draw_initiator.num_indices, nullptr,
xenos::IsMajorModeExplicit(vgt_draw_initiator.major_mode,
vgt_draw_initiator.prim_type));
if (!success) {
XELOGE("PM4_DRAW_INDX_IMM({}, {}): Failed in backend",
vgt_draw_initiator.num_indices,
uint32_t(vgt_draw_initiator.prim_type));
}
return true;
// "draw using supplied indices in packet"
// Generally used by Xbox 360 Direct3D 9 for kAutoIndex source.
// No viz query token.
return ExecutePacketType3Draw(reader, packet, "PM4_DRAW_INDX_2", 0, count);
}
bool CommandProcessor::ExecutePacketType3_SET_CONSTANT(RingBuffer* reader,