mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 16:02:37 +00:00
graphics: draw indexed indirect multi-draws from GPU-read arguments
This commit is contained in:
1 parent
c513a4a13a
commit
949de19bb0
5 files changed
+48
-32
No files matched your search
@@ -886,7 +886,8 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
|||||||
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.
|
// The guest records match Vulkan's indirect commands byte for byte.
|
||||||
EXIT_NOT_IMPLEMENTED(stride_in_bytes < (indexed ? sizeof(vk::DrawIndexedIndirectCommand)
|
EXIT_NOT_IMPLEMENTED((stride_in_bytes & 3u) != 0 ||
|
||||||
|
stride_in_bytes < (indexed ? sizeof(vk::DrawIndexedIndirectCommand)
|
||||||
: sizeof(vk::DrawIndirectCommand)));
|
: sizeof(vk::DrawIndirectCommand)));
|
||||||
|
|
||||||
const auto draw_count = count_addr == nullptr
|
const auto draw_count = count_addr == nullptr
|
||||||
@@ -902,15 +903,14 @@ void CommandProcessor::DrawIndirectMulti(uint32_t data_offset, uint32_t max_coun
|
|||||||
// NUM_INSTANCES keeps the last draw's instance count.
|
// NUM_INSTANCES keeps the last draw's instance count.
|
||||||
m_num_instances_addr = args_addr + static_cast<uint64_t>(draw_count - 1) * stride_in_bytes +
|
m_num_instances_addr = args_addr + static_cast<uint64_t>(draw_count - 1) * stride_in_bytes +
|
||||||
offsetof(vk::DrawIndirectCommand, instanceCount);
|
offsetof(vk::DrawIndirectCommand, instanceCount);
|
||||||
// With no SGPRs to patch, the GPU reads a single indexed draw's arguments if the shaders allow.
|
// With no SGPRs to patch, the GPU reads indexed draws' arguments if the shaders allow.
|
||||||
if (indexed && draw_count == 1 && m_index_buffer_size != 0 &&
|
if (indexed && m_index_buffer_size != 0 && registers.vertex_offset == Pm4::SH_NOP &&
|
||||||
registers.vertex_offset == Pm4::SH_NOP && registers.instance_offset == Pm4::SH_NOP &&
|
registers.instance_offset == Pm4::SH_NOP && registers.index_offset == Pm4::SH_NOP &&
|
||||||
registers.index_offset == Pm4::SH_NOP &&
|
|
||||||
executor.DrawIndex(m_submit_id, CurrentBuffer(),
|
executor.DrawIndex(m_submit_id, CurrentBuffer(),
|
||||||
{.index_count = m_index_buffer_size,
|
{.index_count = m_index_buffer_size,
|
||||||
.index_addr = IndexAddress(0),
|
.index_addr = IndexAddress(0),
|
||||||
.index_type_and_size = m_index_type_and_size,
|
.index_type_and_size = m_index_type_and_size,
|
||||||
.indirect_args = args_addr})) {
|
.indirect = {args_addr, draw_count, stride_in_bytes}})) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,13 @@ enum class DrawOffsetSource : uint8_t {
|
|||||||
IndirectArgs,
|
IndirectArgs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Nonzero address: the GPU reads count VkDrawIndexedIndirectCommand records, stride bytes apart.
|
||||||
|
struct DrawIndirectArgs {
|
||||||
|
uint64_t address = 0;
|
||||||
|
uint32_t count = 0;
|
||||||
|
uint32_t stride = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct DrawIndexArgs {
|
struct DrawIndexArgs {
|
||||||
uint32_t index_count = 0;
|
uint32_t index_count = 0;
|
||||||
const void* index_addr = nullptr;
|
const void* index_addr = nullptr;
|
||||||
@@ -57,8 +64,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.
|
// With indirect records, index_count is INDEX_BUFFER_SIZE.
|
||||||
uint64_t indirect_args = 0;
|
DrawIndirectArgs indirect;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DrawAutoArgs {
|
struct DrawAutoArgs {
|
||||||
@@ -165,7 +172,7 @@ public:
|
|||||||
std::span<PreparedBindings* const> bindings);
|
std::span<PreparedBindings* const> bindings);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// False, with nothing drawn, when the shaders cannot take indirect_args from the GPU.
|
// False, with nothing drawn, when the shaders cannot take indirect records from the GPU.
|
||||||
bool DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const DrawIndexArgs& args);
|
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);
|
||||||
|
|
||||||
|
|||||||
@@ -632,10 +632,10 @@ bool RenderExecutor::ConsumeMetadataColorOperation(CommandBuffer& buffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct DrawEmitInfo {
|
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;
|
DrawIndirectArgs indirect;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DrawIndexBufferSource {
|
struct DrawIndexBufferSource {
|
||||||
@@ -1190,11 +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 {};
|
std::pair<Buffer*, uint64_t> args_buffer {};
|
||||||
if (emit.indirect_args != 0) {
|
if (emit.indirect.address != 0) {
|
||||||
indirect = m_context.GetBufferCache().ObtainBuffer(
|
const auto size = static_cast<uint64_t>(emit.indirect.count - 1) * emit.indirect.stride +
|
||||||
emit.indirect_args, sizeof(vk::DrawIndexedIndirectCommand), false);
|
sizeof(vk::DrawIndexedIndirectCommand);
|
||||||
EXIT_IF((indirect.second & 3u) != 0);
|
args_buffer = m_context.GetBufferCache().ObtainBuffer(emit.indirect.address, size, false);
|
||||||
|
EXIT_IF((args_buffer.second & 3u) != 0);
|
||||||
}
|
}
|
||||||
if (draw.IsIndexed()) {
|
if (draw.IsIndexed()) {
|
||||||
LogDrawPhase(draw.Name(), "CreatePipeline");
|
LogDrawPhase(draw.Name(), "CreatePipeline");
|
||||||
@@ -1282,8 +1283,9 @@ 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) {
|
} else if (args_buffer.first != nullptr) {
|
||||||
vk_buffer.drawIndexedIndirect(indirect.first->Handle(), indirect.second, 1, 0);
|
vk_buffer.drawIndexedIndirect(args_buffer.first->Handle(), args_buffer.second,
|
||||||
|
emit.indirect.count, emit.indirect.stride);
|
||||||
} else {
|
} else {
|
||||||
EmitDrawPrimitives(ucfg, vk_buffer, draw, emit);
|
EmitDrawPrimitives(ucfg, vk_buffer, draw, emit);
|
||||||
}
|
}
|
||||||
@@ -1318,7 +1320,7 @@ bool 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 && args.indirect_args == 0)) {
|
if (args.index_count == 0 || (args.instance_count == 0 && args.indirect.address == 0)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1401,7 +1403,7 @@ bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
|
|||||||
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.
|
// Only embedded fetches apply Vulkan's offsets; mesh and quad-list draws need CPU counts.
|
||||||
if (args.indirect_args != 0 &&
|
if (args.indirect.address != 0 &&
|
||||||
(!state.vertex_info[0].fetch_embedded ||
|
(!state.vertex_info[0].fetch_embedded ||
|
||||||
state.vertex_info[0].stage.program->stage == ShaderType::Mesh ||
|
state.vertex_info[0].stage.program->stage == ShaderType::Mesh ||
|
||||||
ucfg.GetPrimType() == Prospero::PrimitiveType::kQuadListLegacy)) {
|
ucfg.GetPrimType() == Prospero::PrimitiveType::kQuadListLegacy)) {
|
||||||
@@ -1419,7 +1421,7 @@ bool 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;
|
emit.indirect = args.indirect;
|
||||||
|
|
||||||
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source,
|
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source,
|
||||||
primitive_restart);
|
primitive_restart);
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
|
|||||||
check_feature(device_features2.features.tessellationShader, "tessellationShader");
|
check_feature(device_features2.features.tessellationShader, "tessellationShader");
|
||||||
check_feature(device_features2.features.drawIndirectFirstInstance,
|
check_feature(device_features2.features.drawIndirectFirstInstance,
|
||||||
"drawIndirectFirstInstance");
|
"drawIndirectFirstInstance");
|
||||||
|
check_feature(device_features2.features.multiDrawIndirect, "multiDrawIndirect");
|
||||||
|
|
||||||
if (!skip_device) {
|
if (!skip_device) {
|
||||||
auto available_extensions = EnumerateVulkan<vk::ExtensionProperties>(
|
auto available_extensions = EnumerateVulkan<vk::ExtensionProperties>(
|
||||||
@@ -613,6 +614,7 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
|||||||
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.drawIndirectFirstInstance = VK_TRUE;
|
||||||
|
device_features.multiDrawIndirect = 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;
|
||||||
|
|||||||
@@ -18659,9 +18659,9 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Embedded fetches take indirect arguments from the GPU copy, whose vertex offset 7
|
// Embedded fetches take indirect records from the GPU copy: the first draws degenerate
|
||||||
// selects the covering triangle; the CPU backing holds zeros. Other vertex shaders read
|
// vertices 0-2, the second's vertex offset 7 selects the covering triangle. The CPU
|
||||||
// the arguments on the CPU.
|
// backing holds zeros. Other vertex shaders read the records on the CPU.
|
||||||
{
|
{
|
||||||
constexpr uint64_t vertices_address = depth_address + 0x38000;
|
constexpr uint64_t vertices_address = depth_address + 0x38000;
|
||||||
constexpr uint64_t args_address = vertices_address + 0x100;
|
constexpr uint64_t args_address = vertices_address + 0x100;
|
||||||
@@ -18670,14 +18670,17 @@ public:
|
|||||||
std::ranges::copy(triangle, positions.begin() + 28);
|
std::ranges::copy(triangle, positions.begin() + 28);
|
||||||
std::memcpy(reinterpret_cast<void *>(vertices_address), positions.data(),
|
std::memcpy(reinterpret_cast<void *>(vertices_address), positions.data(),
|
||||||
sizeof(positions));
|
sizeof(positions));
|
||||||
const vk::DrawIndexedIndirectCommand args{3, 1, 0, 7, 5};
|
constexpr uint32_t stride = 32;
|
||||||
std::memcpy(reinterpret_cast<void *>(args_address), &args, sizeof(args));
|
const vk::DrawIndexedIndirectCommand degenerate{3, 1, 0, 0, 5};
|
||||||
|
const vk::DrawIndexedIndirectCommand covering{3, 1, 0, 7, 5};
|
||||||
|
std::memcpy(reinterpret_cast<void *>(args_address), °enerate, sizeof(degenerate));
|
||||||
|
std::memcpy(reinterpret_cast<void *>(args_address + stride), &covering, sizeof(covering));
|
||||||
// Larger than a caching page, so the arguments are cached instead of streamed.
|
// Larger than a caching page, so the arguments are cached instead of streamed.
|
||||||
(void)context.GetBufferCache().ObtainBuffer(
|
(void)context.GetBufferCache().ObtainBuffer(
|
||||||
vertices_address, BufferCache::CACHING_PAGESIZE * 2, false);
|
vertices_address, BufferCache::CACHING_PAGESIZE * 2, false);
|
||||||
const vk::DrawIndexedIndirectCommand zero_args{};
|
const std::array<u32, 2 * stride / 4> zeros{};
|
||||||
Require("GpuIndirectDraw", "CPU backing",
|
Require("GpuIndirectDraw", "CPU backing",
|
||||||
LibKernel::Memory::TryWriteBacking(args_address, &zero_args, sizeof(zero_args)),
|
LibKernel::Memory::TryWriteBacking(args_address, zeros.data(), sizeof(zeros)),
|
||||||
"the CPU copy of the arguments could not be cleared");
|
"the CPU copy of the arguments could not be cleared");
|
||||||
static const auto fetch_vertex = [] {
|
static const auto fetch_vertex = [] {
|
||||||
std::vector<u32> code{EncodeSMovB32(0, InlineU32(0)), EncodeSmem0(0x02u, 20, 4),
|
std::vector<u32> code{EncodeSMovB32(0, InlineU32(0)), EncodeSmem0(0x02u, 20, 4),
|
||||||
@@ -18718,7 +18721,7 @@ public:
|
|||||||
}
|
}
|
||||||
const DrawIndexArgs indirect{
|
const DrawIndexArgs indirect{
|
||||||
.index_count = 3, .index_addr = reinterpret_cast<const void *>(index_address),
|
.index_count = 3, .index_addr = reinterpret_cast<const void *>(index_address),
|
||||||
.index_type_and_size = 1, .indirect_args = args_address};
|
.index_type_and_size = 1, .indirect = {args_address, 2, stride}};
|
||||||
shaders.SetEsShaderBase(fetch_vs);
|
shaders.SetEsShaderBase(fetch_vs);
|
||||||
TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), sparse_color.image_id,
|
TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), sparse_color.image_id,
|
||||||
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, {});
|
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, {});
|
||||||
@@ -18730,7 +18733,7 @@ public:
|
|||||||
for (size_t component = 0; component < pixels.size(); component++) {
|
for (size_t component = 0; component < pixels.size(); component++) {
|
||||||
Require("GpuIndirectDraw", "vertex offset",
|
Require("GpuIndirectDraw", "vertex offset",
|
||||||
pixels[component] == (component % 4 == 3 ? 0x3f800000u : 0x3e800000u),
|
pixels[component] == (component % 4 == 3 ? 0x3f800000u : 0x3e800000u),
|
||||||
"the draw did not take its arguments, vertex offset included, from the GPU");
|
"the draw did not take every record, vertex offset included, from the GPU");
|
||||||
}
|
}
|
||||||
shaders.SetEsShaderBase(indirect_vs);
|
shaders.SetEsShaderBase(indirect_vs);
|
||||||
Require("GpuIndirectDraw", "CPU fallback",
|
Require("GpuIndirectDraw", "CPU fallback",
|
||||||
@@ -20773,6 +20776,7 @@ private:
|
|||||||
m_rasterization_supported = available_features.fillModeNonSolid &&
|
m_rasterization_supported = available_features.fillModeNonSolid &&
|
||||||
available_features.tessellationShader &&
|
available_features.tessellationShader &&
|
||||||
available_features.drawIndirectFirstInstance &&
|
available_features.drawIndirectFirstInstance &&
|
||||||
|
available_features.multiDrawIndirect &&
|
||||||
available_features.depthBounds &&
|
available_features.depthBounds &&
|
||||||
available_depth_clip.depthClipEnable &&
|
available_depth_clip.depthClipEnable &&
|
||||||
available_clip_control.depthClipControl &&
|
available_clip_control.depthClipControl &&
|
||||||
@@ -20866,6 +20870,7 @@ private:
|
|||||||
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.drawIndirectFirstInstance = m_rasterization_supported;
|
||||||
|
device_features.multiDrawIndirect = 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