mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-23 04:16:57 +02:00
graphics: draw single indexed indirect draws from GPU-read arguments
This commit is contained in:
@@ -154,6 +154,8 @@ private:
|
|||||||
uint32_t interrupt_context_id);
|
uint32_t interrupt_context_id);
|
||||||
void ProcessPm4(Pm4Execution& execution);
|
void ProcessPm4(Pm4Execution& execution);
|
||||||
void SuspendPm4();
|
void SuspendPm4();
|
||||||
|
uint32_t NumInstances();
|
||||||
|
const void* IndexAddress(uint32_t first_index) const;
|
||||||
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
CommandScheduler& GetScheduler() const { return m_renderer.GetCommandScheduler(); }
|
||||||
CommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
CommandBuffer& CurrentBuffer() { return GetScheduler().Current(); }
|
||||||
|
|
||||||
@@ -169,8 +171,10 @@ private:
|
|||||||
uint64_t m_index_base_addr = 0;
|
uint64_t m_index_base_addr = 0;
|
||||||
uint64_t m_draw_indirect_args_base_addr = 0;
|
uint64_t m_draw_indirect_args_base_addr = 0;
|
||||||
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
uint64_t m_dispatch_indirect_args_base_addr = 0;
|
||||||
// Persistent draw state: indirect draws update it for subsequent draws.
|
// Persistent draw state. Indirect draws leave the address of their last instance count,
|
||||||
uint32_t m_num_instances = 1;
|
// read only when a later draw inherits it.
|
||||||
|
uint32_t m_num_instances = 1;
|
||||||
|
uint64_t m_num_instances_addr = 0;
|
||||||
|
|
||||||
uint32_t m_de_count = 0;
|
uint32_t m_de_count = 0;
|
||||||
uint32_t m_ce_count = 0;
|
uint32_t m_ce_count = 0;
|
||||||
|
|||||||
@@ -39,21 +39,6 @@ static thread_local Pm4Execution* g_current_execution = nullptr;
|
|||||||
static thread_local bool g_gpu_thread = false;
|
static thread_local bool g_gpu_thread = false;
|
||||||
static thread_local GuestGpu* g_gpu_state = nullptr;
|
static thread_local GuestGpu* g_gpu_state = nullptr;
|
||||||
|
|
||||||
struct DrawIndirectArgs {
|
|
||||||
uint32_t vertex_count_per_instance;
|
|
||||||
uint32_t instance_count;
|
|
||||||
uint32_t start_vertex_location;
|
|
||||||
uint32_t start_instance_location;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DrawIndexedIndirectArgs {
|
|
||||||
uint32_t index_count_per_instance;
|
|
||||||
uint32_t instance_count;
|
|
||||||
uint32_t start_index_location;
|
|
||||||
uint32_t base_vertex_location;
|
|
||||||
uint32_t start_instance_location;
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool GraphicsRunDebugDumpEnabled() {
|
static bool GraphicsRunDebugDumpEnabled() {
|
||||||
return Config::GraphicsDebugDumpEnabled() &&
|
return Config::GraphicsDebugDumpEnabled() &&
|
||||||
Config::GetPrintfDirection() != Config::LogDirection::Silent;
|
Config::GetPrintfDirection() != Config::LogDirection::Silent;
|
||||||
@@ -206,6 +191,7 @@ void CommandProcessor::Reset() {
|
|||||||
m_index_buffer_size = 0;
|
m_index_buffer_size = 0;
|
||||||
m_index_base_addr = 0;
|
m_index_base_addr = 0;
|
||||||
m_num_instances = 1;
|
m_num_instances = 1;
|
||||||
|
m_num_instances_addr = 0;
|
||||||
m_predicate_skip = false;
|
m_predicate_skip = false;
|
||||||
m_user_data_marker = HW::UserSgprType::Unknown;
|
m_user_data_marker = HW::UserSgprType::Unknown;
|
||||||
m_draw_indirect_args_base_addr = 0;
|
m_draw_indirect_args_base_addr = 0;
|
||||||
@@ -784,7 +770,16 @@ void CommandProcessor::SetNumInstances(uint32_t num_instances) {
|
|||||||
num_instances = 1;
|
num_instances = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_num_instances = num_instances;
|
m_num_instances = num_instances;
|
||||||
|
m_num_instances_addr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CommandProcessor::NumInstances() {
|
||||||
|
if (m_num_instances_addr != 0) {
|
||||||
|
m_num_instances = *reinterpret_cast<const uint32_t*>(m_num_instances_addr);
|
||||||
|
m_num_instances_addr = 0;
|
||||||
|
}
|
||||||
|
return m_num_instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::SetPredication(uint32_t condition, uint32_t op, uint32_t wait_op,
|
void CommandProcessor::SetPredication(uint32_t condition, uint32_t op, uint32_t wait_op,
|
||||||
@@ -841,16 +836,12 @@ void CommandProcessor::SetPredication(uint32_t condition, uint32_t op, uint32_t
|
|||||||
void CommandProcessor::DrawIndex(DrawIndexArgs args) {
|
void CommandProcessor::DrawIndex(DrawIndexArgs args) {
|
||||||
args.index_type_and_size = m_index_type_and_size;
|
args.index_type_and_size = m_index_type_and_size;
|
||||||
if (args.instance_count == 0) {
|
if (args.instance_count == 0) {
|
||||||
args.instance_count = m_num_instances;
|
args.instance_count = NumInstances();
|
||||||
}
|
|
||||||
if (GraphicsRunDebugDumpEnabled() && (args.base_vertex != 0 || args.first_instance != 0)) {
|
|
||||||
LOGF("\t draw indexed offsets: base_vertex = %" PRId32 ", first_instance = %" PRIu32 "\n",
|
|
||||||
args.base_vertex, args.first_instance);
|
|
||||||
}
|
}
|
||||||
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(), args);
|
m_renderer.GetRenderExecutor().DrawIndex(m_submit_id, CurrentBuffer(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_count) {
|
const void* CommandProcessor::IndexAddress(uint32_t first_index) const {
|
||||||
uint64_t index_size = 0;
|
uint64_t index_size = 0;
|
||||||
switch (m_index_type_and_size) {
|
switch (m_index_type_and_size) {
|
||||||
case 0: index_size = 2; break;
|
case 0: index_size = 2; break;
|
||||||
@@ -858,11 +849,11 @@ void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_cou
|
|||||||
case 2: index_size = 1; break;
|
case 2: index_size = 1; break;
|
||||||
default: EXIT("unknown index_type_and_size: %u\n", m_index_type_and_size);
|
default: EXIT("unknown index_type_and_size: %u\n", m_index_type_and_size);
|
||||||
}
|
}
|
||||||
|
return reinterpret_cast<const void*>(m_index_base_addr + first_index * index_size);
|
||||||
|
}
|
||||||
|
|
||||||
auto* index_addr = reinterpret_cast<const void*>(
|
void CommandProcessor::DrawIndexOffset(uint32_t index_offset, uint32_t index_count) {
|
||||||
m_index_base_addr + static_cast<uint64_t>(index_offset) * index_size);
|
DrawIndex({.index_count = index_count, .index_addr = IndexAddress(index_offset)});
|
||||||
|
|
||||||
DrawIndex({.index_count = index_count, .index_addr = index_addr});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PatchIndirectDrawOffsets(CommandProcessor& cp, IndirectDrawRegisters registers,
|
static void PatchIndirectDrawOffsets(CommandProcessor& cp, IndirectDrawRegisters registers,
|
||||||
@@ -883,7 +874,8 @@ static void PatchIndirectDrawOffsets(CommandProcessor& cp, IndirectDrawRegisters
|
|||||||
|
|
||||||
void CommandProcessor::DrawIndirect(uint32_t data_offset, IndirectDrawRegisters registers,
|
void CommandProcessor::DrawIndirect(uint32_t data_offset, IndirectDrawRegisters registers,
|
||||||
uint32_t draw_initiator, bool indexed) {
|
uint32_t draw_initiator, bool indexed) {
|
||||||
const auto args_size = indexed ? sizeof(DrawIndexedIndirectArgs) : sizeof(DrawIndirectArgs);
|
const auto args_size =
|
||||||
|
indexed ? sizeof(vk::DrawIndexedIndirectCommand) : sizeof(vk::DrawIndirectCommand);
|
||||||
DrawIndirectMulti(data_offset, 1, nullptr, args_size, registers, draw_initiator, indexed);
|
DrawIndirectMulti(data_offset, 1, nullptr, args_size, registers, draw_initiator, indexed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -893,77 +885,61 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
|||||||
uint32_t draw_initiator, bool indexed) {
|
uint32_t draw_initiator, bool indexed) {
|
||||||
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != (indexed ? 0u : 2u));
|
EXIT_NOT_IMPLEMENTED((draw_initiator & ~0x20u) != (indexed ? 0u : 2u));
|
||||||
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
EXIT_NOT_IMPLEMENTED(m_draw_indirect_args_base_addr == 0);
|
||||||
|
// The guest records match Vulkan's indirect commands byte for byte.
|
||||||
|
EXIT_NOT_IMPLEMENTED(stride_in_bytes < (indexed ? sizeof(vk::DrawIndexedIndirectCommand)
|
||||||
|
: sizeof(vk::DrawIndirectCommand)));
|
||||||
|
|
||||||
uint32_t draw_count = max_count_or_count;
|
const auto draw_count = count_addr == nullptr
|
||||||
if (count_addr != nullptr) {
|
? max_count_or_count
|
||||||
draw_count = *count_addr;
|
: std::min(uint32_t {*count_addr}, max_count_or_count);
|
||||||
if (draw_count > max_count_or_count) {
|
|
||||||
draw_count = max_count_or_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (draw_count == 0) {
|
if (draw_count == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Common::DebugCounters::Add(Common::DebugCounters::Counter::IndirectDraws, draw_count);
|
Common::DebugCounters::Add(Common::DebugCounters::Counter::IndirectDraws, draw_count);
|
||||||
|
|
||||||
const auto args_size = indexed ? sizeof(DrawIndexedIndirectArgs) : sizeof(DrawIndirectArgs);
|
auto& executor = m_renderer.GetRenderExecutor();
|
||||||
EXIT_NOT_IMPLEMENTED(stride_in_bytes < args_size);
|
const auto args_addr = m_draw_indirect_args_base_addr + data_offset;
|
||||||
|
// NUM_INSTANCES keeps the last draw's instance count.
|
||||||
uint64_t index_size = 0;
|
m_num_instances_addr = args_addr + static_cast<uint64_t>(draw_count - 1) * stride_in_bytes +
|
||||||
if (indexed) {
|
offsetof(vk::DrawIndirectCommand, instanceCount);
|
||||||
switch (m_index_type_and_size) {
|
// With no SGPRs to patch, the GPU reads a single indexed draw's arguments if the shaders allow.
|
||||||
case 0: index_size = 2; break;
|
if (indexed && draw_count == 1 && m_index_buffer_size != 0 &&
|
||||||
case 1: index_size = 4; break;
|
registers.vertex_offset == Pm4::SH_NOP && registers.instance_offset == Pm4::SH_NOP &&
|
||||||
case 2: index_size = 1; break;
|
registers.index_offset == Pm4::SH_NOP &&
|
||||||
default: EXIT("unknown index_type_and_size: %u\n", m_index_type_and_size);
|
executor.DrawIndex(m_submit_id, CurrentBuffer(),
|
||||||
}
|
{.index_count = m_index_buffer_size,
|
||||||
|
.index_addr = IndexAddress(0),
|
||||||
|
.index_type_and_size = m_index_type_and_size,
|
||||||
|
.indirect_args = args_addr})) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32_t max_indices = m_index_buffer_size != 0 ? m_index_buffer_size : UINT32_MAX;
|
||||||
for (uint32_t i = 0; i < draw_count; i++) {
|
for (uint32_t i = 0; i < draw_count; i++) {
|
||||||
const auto args_addr = m_draw_indirect_args_base_addr + data_offset +
|
const auto addr = args_addr + static_cast<uint64_t>(i) * stride_in_bytes;
|
||||||
static_cast<uint64_t>(i) * stride_in_bytes;
|
|
||||||
|
|
||||||
if (!indexed) {
|
if (!indexed) {
|
||||||
auto* args = reinterpret_cast<const DrawIndirectArgs*>(args_addr);
|
const auto& args = *reinterpret_cast<const vk::DrawIndirectCommand*>(addr);
|
||||||
PatchIndirectDrawOffsets(*this, registers, args->start_vertex_location,
|
PatchIndirectDrawOffsets(*this, registers, args.firstVertex, args.firstInstance, 0);
|
||||||
args->start_instance_location, 0);
|
executor.DrawAuto(m_submit_id, CurrentBuffer(),
|
||||||
m_num_instances = args->instance_count;
|
{.vertex_count = args.vertexCount,
|
||||||
DrawIndexAuto({.vertex_count = args->vertex_count_per_instance,
|
.instance_count = args.instanceCount,
|
||||||
.instance_count = args->instance_count,
|
.first_vertex = args.firstVertex,
|
||||||
.first_vertex = args->start_vertex_location,
|
.first_instance = args.firstInstance,
|
||||||
.first_instance = args->start_instance_location,
|
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* args = reinterpret_cast<const DrawIndexedIndirectArgs*>(args_addr);
|
const auto& args = *reinterpret_cast<const vk::DrawIndexedIndirectCommand*>(addr);
|
||||||
PatchIndirectDrawOffsets(*this, registers, args->base_vertex_location,
|
PatchIndirectDrawOffsets(*this, registers, static_cast<uint32_t>(args.vertexOffset),
|
||||||
args->start_instance_location, args->start_index_location);
|
args.firstInstance, args.firstIndex);
|
||||||
|
executor.DrawIndex(m_submit_id, CurrentBuffer(),
|
||||||
auto* index_addr = reinterpret_cast<const void*>(
|
{.index_count = std::min(args.indexCount, max_indices),
|
||||||
m_index_base_addr + static_cast<uint64_t>(args->start_index_location) * index_size);
|
.index_addr = IndexAddress(args.firstIndex),
|
||||||
|
.instance_count = args.instanceCount,
|
||||||
const uint32_t index_count =
|
.index_type_and_size = m_index_type_and_size,
|
||||||
(m_index_buffer_size != 0
|
.base_vertex = args.vertexOffset,
|
||||||
? std::min(args->index_count_per_instance, m_index_buffer_size)
|
.first_instance = args.firstInstance,
|
||||||
: args->index_count_per_instance);
|
.offset_source = DrawOffsetSource::IndirectArgs});
|
||||||
if (GraphicsRunDebugDumpEnabled() && index_count != args->index_count_per_instance) {
|
|
||||||
static std::atomic<uint32_t> log_count {0};
|
|
||||||
if (log_count.fetch_add(1, std::memory_order_relaxed) < 64) {
|
|
||||||
LOGF("\t DrawIndexIndirectMulti: clamped index_count from %" PRIu32 " to %" PRIu32
|
|
||||||
" using INDEX_BUFFER_SIZE\n",
|
|
||||||
args->index_count_per_instance, index_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_num_instances = args->instance_count;
|
|
||||||
DrawIndex({.index_count = index_count,
|
|
||||||
.index_addr = index_addr,
|
|
||||||
.instance_count = args->instance_count,
|
|
||||||
.base_vertex = static_cast<int32_t>(args->base_vertex_location),
|
|
||||||
.first_instance = args->start_instance_location,
|
|
||||||
.offset_source = DrawOffsetSource::IndirectArgs});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1012,7 +988,7 @@ void CommandProcessor::DispatchIndirect(uint64_t args_addr, uint32_t mode) {
|
|||||||
|
|
||||||
void CommandProcessor::DrawIndexAuto(DrawAutoArgs args) {
|
void CommandProcessor::DrawIndexAuto(DrawAutoArgs args) {
|
||||||
if (args.instance_count == 0) {
|
if (args.instance_count == 0) {
|
||||||
args.instance_count = m_num_instances;
|
args.instance_count = NumInstances();
|
||||||
}
|
}
|
||||||
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), args);
|
m_renderer.GetRenderExecutor().DrawAuto(m_submit_id, CurrentBuffer(), args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ vk::MemoryBarrier MakeShaderWriteDependency() {
|
|||||||
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite |
|
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite |
|
||||||
vk::AccessFlagBits::eVertexAttributeRead |
|
vk::AccessFlagBits::eVertexAttributeRead |
|
||||||
vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eUniformRead |
|
vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eUniformRead |
|
||||||
|
vk::AccessFlagBits::eIndirectCommandRead |
|
||||||
vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite |
|
vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite |
|
||||||
vk::AccessFlagBits::eColorAttachmentRead |
|
vk::AccessFlagBits::eColorAttachmentRead |
|
||||||
vk::AccessFlagBits::eColorAttachmentWrite;
|
vk::AccessFlagBits::eColorAttachmentWrite;
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ struct DrawIndexArgs {
|
|||||||
uint32_t first_instance = 0;
|
uint32_t first_instance = 0;
|
||||||
DrawOffsetSource offset_source = DrawOffsetSource::DrawState;
|
DrawOffsetSource offset_source = DrawOffsetSource::DrawState;
|
||||||
uint32_t render_target_slice_offset = 0;
|
uint32_t render_target_slice_offset = 0;
|
||||||
|
// Nonzero: the GPU reads this VkDrawIndexedIndirectCommand; index_count is INDEX_BUFFER_SIZE.
|
||||||
|
uint64_t indirect_args = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DrawAutoArgs {
|
struct DrawAutoArgs {
|
||||||
@@ -163,7 +165,8 @@ public:
|
|||||||
std::span<PreparedBindings* const> bindings);
|
std::span<PreparedBindings* const> bindings);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const DrawIndexArgs& args);
|
// False, with nothing drawn, when the shaders cannot take indirect_args from the GPU.
|
||||||
|
bool DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const DrawIndexArgs& args);
|
||||||
void DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const DrawAutoArgs& args);
|
void DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const DrawAutoArgs& args);
|
||||||
|
|
||||||
struct GraphicsBindings {
|
struct GraphicsBindings {
|
||||||
|
|||||||
@@ -635,6 +635,7 @@ struct DrawEmitInfo {
|
|||||||
int32_t vertex_offset = 0;
|
int32_t vertex_offset = 0;
|
||||||
uint32_t first_vertex = 0;
|
uint32_t first_vertex = 0;
|
||||||
uint32_t first_instance = 0;
|
uint32_t first_instance = 0;
|
||||||
|
uint64_t indirect_args = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DrawIndexBufferSource {
|
struct DrawIndexBufferSource {
|
||||||
@@ -1189,6 +1190,12 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
|||||||
vertex_bindings = AcquireVertexBuffers(buffer, state.vertex_info[0]);
|
vertex_bindings = AcquireVertexBuffers(buffer, state.vertex_info[0]);
|
||||||
index_binding = PrepareIndexBuffer(buffer, index_source);
|
index_binding = PrepareIndexBuffer(buffer, index_source);
|
||||||
}
|
}
|
||||||
|
std::pair<Buffer*, uint64_t> indirect {};
|
||||||
|
if (emit.indirect_args != 0) {
|
||||||
|
indirect = m_context.GetBufferCache().ObtainBuffer(
|
||||||
|
emit.indirect_args, sizeof(vk::DrawIndexedIndirectCommand), false);
|
||||||
|
EXIT_IF((indirect.second & 3u) != 0);
|
||||||
|
}
|
||||||
if (draw.IsIndexed()) {
|
if (draw.IsIndexed()) {
|
||||||
LogDrawPhase(draw.Name(), "CreatePipeline");
|
LogDrawPhase(draw.Name(), "CreatePipeline");
|
||||||
}
|
}
|
||||||
@@ -1275,6 +1282,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
|||||||
}
|
}
|
||||||
if (mesh_active) {
|
if (mesh_active) {
|
||||||
vk_buffer.drawMeshTasksEXT(mesh_groups, draw.instance_count, 1);
|
vk_buffer.drawMeshTasksEXT(mesh_groups, draw.instance_count, 1);
|
||||||
|
} else if (indirect.first != nullptr) {
|
||||||
|
vk_buffer.drawIndexedIndirect(indirect.first->Handle(), indirect.second, 1, 0);
|
||||||
} else {
|
} else {
|
||||||
EmitDrawPrimitives(ucfg, vk_buffer, draw, emit);
|
EmitDrawPrimitives(ucfg, vk_buffer, draw, emit);
|
||||||
}
|
}
|
||||||
@@ -1294,7 +1303,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
||||||
const DrawIndexArgs& args) {
|
const DrawIndexArgs& args) {
|
||||||
KYTY_PROFILER_FUNCTION();
|
KYTY_PROFILER_FUNCTION();
|
||||||
|
|
||||||
@@ -1309,19 +1318,19 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
reinterpret_cast<uint64_t>(args.index_addr));
|
reinterpret_cast<uint64_t>(args.index_addr));
|
||||||
|
|
||||||
Common::LockGuard lock(m_context.GetMutex());
|
Common::LockGuard lock(m_context.GetMutex());
|
||||||
if (args.index_count == 0 || args.instance_count == 0) {
|
if (args.index_count == 0 || (args.instance_count == 0 && args.indirect_args == 0)) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ConsumeMetadataColorOperation(buffer, args.render_target_slice_offset) ||
|
if (ConsumeMetadataColorOperation(buffer, args.render_target_slice_offset) ||
|
||||||
DepthStencilCopy(buffer) ||
|
DepthStencilCopy(buffer) ||
|
||||||
ResolveColorTargets(buffer, args.render_target_slice_offset)) {
|
ResolveColorTargets(buffer, args.render_target_slice_offset)) {
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DrawHasValidVertexShader(sh_ctx)) {
|
if (!DrawHasValidVertexShader(sh_ctx)) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (graphics_debug_dump_enabled()) {
|
if (graphics_debug_dump_enabled()) {
|
||||||
@@ -1347,7 +1356,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
|
|
||||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||||
if (!GetDrawTopology(ucfg, topology)) {
|
if (!GetDrawTopology(ucfg, topology)) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DrawIndexBufferSource index_source {};
|
DrawIndexBufferSource index_source {};
|
||||||
@@ -1386,11 +1395,19 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
DrawRenderState state {};
|
DrawRenderState state {};
|
||||||
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) {
|
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) {
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogDrawStateIfNeeded(buffer, draw, state, args.index_type_and_size,
|
LogDrawStateIfNeeded(buffer, draw, state, args.index_type_and_size,
|
||||||
args.index_addr);
|
args.index_addr);
|
||||||
|
// Only embedded fetches apply Vulkan's offsets; mesh and quad-list draws need CPU counts.
|
||||||
|
if (args.indirect_args != 0 &&
|
||||||
|
(!state.vertex_info[0].fetch_embedded ||
|
||||||
|
state.vertex_info[0].stage.program->stage == ShaderType::Mesh ||
|
||||||
|
ucfg.GetPrimType() == Prospero::PrimitiveType::kQuadListLegacy)) {
|
||||||
|
ResetBindings();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const bool indirect = args.offset_source == DrawOffsetSource::IndirectArgs;
|
const bool indirect = args.offset_source == DrawOffsetSource::IndirectArgs;
|
||||||
const auto [vertex_offset, instance_offset] =
|
const auto [vertex_offset, instance_offset] =
|
||||||
@@ -1402,10 +1419,12 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
const bool native_indirect = indirect && !state.vertex_info[0].fetch_embedded;
|
const bool native_indirect = indirect && !state.vertex_info[0].fetch_embedded;
|
||||||
emit.vertex_offset = native_indirect ? 0 : vertex_offset + args.base_vertex;
|
emit.vertex_offset = native_indirect ? 0 : vertex_offset + args.base_vertex;
|
||||||
emit.first_instance = native_indirect ? 0 : instance_offset;
|
emit.first_instance = native_indirect ? 0 : instance_offset;
|
||||||
|
emit.indirect_args = args.indirect_args;
|
||||||
|
|
||||||
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source,
|
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source,
|
||||||
primitive_restart);
|
primitive_restart);
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
|
||||||
|
|||||||
@@ -328,6 +328,8 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
|||||||
|
|
||||||
check_feature(device_features2.features.independentBlend, "independentBlend");
|
check_feature(device_features2.features.independentBlend, "independentBlend");
|
||||||
check_feature(device_features2.features.tessellationShader, "tessellationShader");
|
check_feature(device_features2.features.tessellationShader, "tessellationShader");
|
||||||
|
check_feature(device_features2.features.drawIndirectFirstInstance,
|
||||||
|
"drawIndirectFirstInstance");
|
||||||
|
|
||||||
if (!skip_device) {
|
if (!skip_device) {
|
||||||
auto available_extensions = EnumerateVulkan<vk::ExtensionProperties>(
|
auto available_extensions = EnumerateVulkan<vk::ExtensionProperties>(
|
||||||
@@ -610,6 +612,7 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
|||||||
device_features.independentBlend = VK_TRUE;
|
device_features.independentBlend = VK_TRUE;
|
||||||
device_features.dualSrcBlend = VK_TRUE;
|
device_features.dualSrcBlend = VK_TRUE;
|
||||||
device_features.tessellationShader = VK_TRUE;
|
device_features.tessellationShader = VK_TRUE;
|
||||||
|
device_features.drawIndirectFirstInstance = VK_TRUE;
|
||||||
device_features.sampleRateShading = VK_TRUE;
|
device_features.sampleRateShading = VK_TRUE;
|
||||||
device_features.depthBiasClamp = VK_TRUE;
|
device_features.depthBiasClamp = VK_TRUE;
|
||||||
device_features.shaderClipDistance = VK_TRUE;
|
device_features.shaderClipDistance = VK_TRUE;
|
||||||
|
|||||||
@@ -399,9 +399,9 @@ struct TextureCacheTestAccess {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct RenderExecutorTestAccess {
|
struct RenderExecutorTestAccess {
|
||||||
static void DrawIndex(RenderExecutor &executor, CommandBuffer &command,
|
static bool DrawIndex(RenderExecutor &executor, CommandBuffer &command,
|
||||||
const DrawIndexArgs &args) {
|
const DrawIndexArgs &args) {
|
||||||
executor.DrawIndex(0, command, args);
|
return executor.DrawIndex(0, command, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void DrawAuto(RenderExecutor &executor, CommandBuffer &command,
|
static void DrawAuto(RenderExecutor &executor, CommandBuffer &command,
|
||||||
@@ -18659,6 +18659,85 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Embedded fetches take indirect arguments from the GPU copy, whose vertex offset 7
|
||||||
|
// selects the covering triangle; the CPU backing holds zeros. Other vertex shaders read
|
||||||
|
// the arguments on the CPU.
|
||||||
|
{
|
||||||
|
constexpr uint64_t vertices_address = depth_address + 0x38000;
|
||||||
|
constexpr uint64_t args_address = vertices_address + 0x100;
|
||||||
|
std::array<float, 40> positions{};
|
||||||
|
constexpr std::array<float, 12> triangle{-1, -1, 0, 1, 3, -1, 0, 1, -1, 3, 0, 1};
|
||||||
|
std::ranges::copy(triangle, positions.begin() + 28);
|
||||||
|
std::memcpy(reinterpret_cast<void *>(vertices_address), positions.data(),
|
||||||
|
sizeof(positions));
|
||||||
|
const vk::DrawIndexedIndirectCommand args{3, 1, 0, 7, 5};
|
||||||
|
std::memcpy(reinterpret_cast<void *>(args_address), &args, sizeof(args));
|
||||||
|
// Larger than a caching page, so the arguments are cached instead of streamed.
|
||||||
|
(void)context.GetBufferCache().ObtainBuffer(
|
||||||
|
vertices_address, BufferCache::CACHING_PAGESIZE * 2, false);
|
||||||
|
const vk::DrawIndexedIndirectCommand zero_args{};
|
||||||
|
Require("GpuIndirectDraw", "CPU backing",
|
||||||
|
LibKernel::Memory::TryWriteBacking(args_address, &zero_args, sizeof(zero_args)),
|
||||||
|
"the CPU copy of the arguments could not be cleared");
|
||||||
|
static const auto fetch_vertex = [] {
|
||||||
|
std::vector<u32> code{EncodeSMovB32(0, InlineU32(0)), EncodeSmem0(0x02u, 20, 4),
|
||||||
|
EncodeSmem1(0), EncodeVop2(0x01u, 0, Vgpr(8), 5),
|
||||||
|
EncodeMubuf0(0x03u, 0, true), EncodeMubuf1(9, 5, 0),
|
||||||
|
EncodeExp0(0x0c, 0xf), EncodeExp1(9, 10, 11, 12)};
|
||||||
|
code.insert(code.end(), std::ranges::find(native_vertex, EncodeExp0(0x20, 0xf)),
|
||||||
|
native_vertex.end());
|
||||||
|
return code;
|
||||||
|
}();
|
||||||
|
static std::array<uint16_t, static_cast<size_t>(AgcDirectResourceType::Last) + 1>
|
||||||
|
fetch_offsets;
|
||||||
|
fetch_offsets.fill(AGC_ILLEGAL_DIRECT_OFFSET);
|
||||||
|
fetch_offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexBufferTable)] = 0;
|
||||||
|
fetch_offsets[static_cast<size_t>(AgcDirectResourceType::PtrVertexAttribDescTable)] = 2;
|
||||||
|
static ShaderUserData fetch_user_data{
|
||||||
|
.direct_resource_offset = fetch_offsets.data(),
|
||||||
|
.direct_resource_count = static_cast<uint16_t>(fetch_offsets.size())};
|
||||||
|
static ShaderSemantic semantic{.hardware_mapping = 9, .size_in_elements = 4};
|
||||||
|
static const uint32_t vertex_buffer[]{
|
||||||
|
static_cast<u32>(vertices_address),
|
||||||
|
static_cast<u32>(vertices_address >> 32u) | (16u << 16u), 10,
|
||||||
|
DstSel(4, 5, 6, 7) |
|
||||||
|
(static_cast<u32>(Prospero::BufferFormat::k32_32_32_32Float) << 12u)};
|
||||||
|
static const uint32_t attribute =
|
||||||
|
static_cast<u32>(Prospero::VertexAttribFormat::k32_32_32_32Float) << 5u;
|
||||||
|
const auto fetch_vs = reinterpret_cast<uint64_t>(fetch_vertex.data());
|
||||||
|
ShaderMapUserData(fetch_vs,
|
||||||
|
{.type = Prospero::ShaderBinaryType::kGs, .user_data = &fetch_user_data,
|
||||||
|
.input_semantics = &semantic, .num_input_semantics = 1,
|
||||||
|
.code_size_bytes = static_cast<uint32_t>(fetch_vertex.size() * sizeof(u32))});
|
||||||
|
const uint64_t tables[]{reinterpret_cast<uint64_t>(vertex_buffer),
|
||||||
|
reinterpret_cast<uint64_t>(&attribute)};
|
||||||
|
shaders.SetGsShaderResource2({.user_sgpr = 4});
|
||||||
|
for (u32 i = 0; i < 4; i++) {
|
||||||
|
shaders.SetGsUserSgpr(i, static_cast<u32>(tables[i / 2] >> (i % 2 * 32)),
|
||||||
|
HW::UserSgprType::Unknown);
|
||||||
|
}
|
||||||
|
const DrawIndexArgs indirect{
|
||||||
|
.index_count = 3, .index_addr = reinterpret_cast<const void *>(index_address),
|
||||||
|
.index_type_and_size = 1, .indirect_args = args_address};
|
||||||
|
shaders.SetEsShaderBase(fetch_vs);
|
||||||
|
TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), sparse_color.image_id,
|
||||||
|
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, {});
|
||||||
|
Require("GpuIndirectDraw", "embedded fetch",
|
||||||
|
RenderExecutorTestAccess::DrawIndex(executor, scheduler.Current(), indirect),
|
||||||
|
"an embedded-fetch indirect draw fell back to CPU arguments");
|
||||||
|
const auto pixels = ReadCachedTexel(name, context, sparse_color.image_id,
|
||||||
|
{}, {extent, extent, 1});
|
||||||
|
for (size_t component = 0; component < pixels.size(); component++) {
|
||||||
|
Require("GpuIndirectDraw", "vertex offset",
|
||||||
|
pixels[component] == (component % 4 == 3 ? 0x3f800000u : 0x3e800000u),
|
||||||
|
"the draw did not take its arguments, vertex offset included, from the GPU");
|
||||||
|
}
|
||||||
|
shaders.SetEsShaderBase(indirect_vs);
|
||||||
|
Require("GpuIndirectDraw", "CPU fallback",
|
||||||
|
!RenderExecutorTestAccess::DrawIndex(executor, scheduler.Current(), indirect),
|
||||||
|
"GPU-read arguments reached a shader without embedded fetch");
|
||||||
|
}
|
||||||
|
|
||||||
// A fourth VS invocation reads beyond the descriptor instead of reconstructing the corner.
|
// A fourth VS invocation reads beyond the descriptor instead of reconstructing the corner.
|
||||||
const ShaderBufferResource rect_buffer{{
|
const ShaderBufferResource rect_buffer{{
|
||||||
static_cast<u32>(rect_address),
|
static_cast<u32>(rect_address),
|
||||||
@@ -20693,6 +20772,7 @@ private:
|
|||||||
"vertex layer output is not supported");
|
"vertex layer output is not supported");
|
||||||
m_rasterization_supported = available_features.fillModeNonSolid &&
|
m_rasterization_supported = available_features.fillModeNonSolid &&
|
||||||
available_features.tessellationShader &&
|
available_features.tessellationShader &&
|
||||||
|
available_features.drawIndirectFirstInstance &&
|
||||||
available_features.depthBounds &&
|
available_features.depthBounds &&
|
||||||
available_depth_clip.depthClipEnable &&
|
available_depth_clip.depthClipEnable &&
|
||||||
available_clip_control.depthClipControl &&
|
available_clip_control.depthClipControl &&
|
||||||
@@ -20785,6 +20865,7 @@ private:
|
|||||||
device_features.shaderFloat64 = available_features.shaderFloat64;
|
device_features.shaderFloat64 = available_features.shaderFloat64;
|
||||||
device_features.fillModeNonSolid = m_rasterization_supported;
|
device_features.fillModeNonSolid = m_rasterization_supported;
|
||||||
device_features.tessellationShader = m_rasterization_supported;
|
device_features.tessellationShader = m_rasterization_supported;
|
||||||
|
device_features.drawIndirectFirstInstance = m_rasterization_supported;
|
||||||
device_features.depthBounds = m_rasterization_supported;
|
device_features.depthBounds = m_rasterization_supported;
|
||||||
device_info.pEnabledFeatures = &device_features;
|
device_info.pEnabledFeatures = &device_features;
|
||||||
std::vector<const char *> device_extensions{
|
std::vector<const char *> device_extensions{
|
||||||
|
|||||||
Reference in New Issue
Block a user