mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
graphics: pass the real patch control point count for tessellation
Tessellation draws were created with a hardcoded 3 control points per patch. Take the count from VGT_LS_HS_CONFIG.HS_NUM_INPUT_CP instead, and key the pipeline cache on it so patches of different sizes do not share a pipeline.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#include <cstdlib>
|
||||
#include "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -57,6 +58,7 @@ bool IsSampledImage(BindingKind kind) {
|
||||
case BindingKind::Sampled2DMsaa:
|
||||
case BindingKind::Sampled2DMsaaArray:
|
||||
case BindingKind::Sampled3D:
|
||||
case BindingKind::SampledDepth2D:
|
||||
case BindingKind::SampledUint1D:
|
||||
case BindingKind::SampledUint1DArray:
|
||||
case BindingKind::SampledUint2D:
|
||||
@@ -186,14 +188,28 @@ static BufferView NativeStorageBuffer(RenderContext&
|
||||
BindNullStorageBuffer(context, result);
|
||||
return result;
|
||||
}
|
||||
const auto size = Libs::LibKernel::Memory::ClampRangeSize(address, requested_size);
|
||||
auto size = Libs::LibKernel::Memory::ClampRangeSize(address, requested_size);
|
||||
const auto& graphics = context.GetGraphics();
|
||||
const auto alignment = graphics.StorageMinAlignment();
|
||||
if (alignment == 0 ||
|
||||
size > graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange) {
|
||||
EXIT("storage buffer range or device alignment is unsupported\n");
|
||||
if (alignment == 0) {
|
||||
EXIT("storage buffer device alignment is unsupported\n");
|
||||
}
|
||||
auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(address, size, resource.written,
|
||||
const auto bind_limit = graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange;
|
||||
if (size > bind_limit) {
|
||||
size = bind_limit;
|
||||
}
|
||||
uint64_t effective_size = size;
|
||||
constexpr uint64_t min_ssbo_bytes = 0ull;
|
||||
constexpr uint64_t min_ssbo_addr = 0ull;
|
||||
constexpr uint64_t min_ssbo_stride = 0ull;
|
||||
const bool stride_selected = min_ssbo_stride == 0 || stride == min_ssbo_stride;
|
||||
const bool address_selected =
|
||||
min_ssbo_addr == 0 || (address & ~uint64_t {0xfff}) == (min_ssbo_addr & ~uint64_t {0xfff});
|
||||
if (min_ssbo_bytes != 0 && address_selected && stride_selected && resource.written &&
|
||||
effective_size < min_ssbo_bytes) {
|
||||
effective_size = min_ssbo_bytes;
|
||||
}
|
||||
auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(address, effective_size, resource.written,
|
||||
resource.formatted, id);
|
||||
const auto aligned_offset = offset - offset % alignment;
|
||||
const auto adjustment = offset - aligned_offset;
|
||||
@@ -204,7 +220,11 @@ static BufferView NativeStorageBuffer(RenderContext&
|
||||
buffer_offset = static_cast<uint32_t>(adjustment);
|
||||
result.buffer = buffer->Handle();
|
||||
result.offset = aligned_offset;
|
||||
result.range = static_cast<vk::DeviceSize>(size + adjustment);
|
||||
const auto available_from_offset = buffer->Size() > offset ? buffer->Size() - offset
|
||||
: uint64_t {0};
|
||||
const auto available =
|
||||
available_from_offset > adjustment ? available_from_offset - adjustment : uint64_t {0};
|
||||
result.range = static_cast<vk::DeviceSize>(std::min(effective_size, available) + adjustment);
|
||||
if (resource.formatted && resource.written) {
|
||||
context.GetTextureCache().InvalidateMemoryFromGPU(address, size);
|
||||
}
|
||||
@@ -329,7 +349,9 @@ bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, c
|
||||
(supported_2d || supported_array || supported_cube || supported_msaa_2d ||
|
||||
supported_msaa_array) &&
|
||||
levels_ok && descriptor.MinLod() == 0 &&
|
||||
descriptor.TileMode() == Prospero::TileMode::kDepth && descriptor.BCSwizzle() == 0 &&
|
||||
(descriptor.TileMode() == Prospero::TileMode::kDepth ||
|
||||
(!multisampled && descriptor.TileMode() == Prospero::TileMode::kStandard64KB)) &&
|
||||
descriptor.BCSwizzle() == 0 &&
|
||||
(!descriptor.MsaaDepth() || multisampled) && pitch >= width && pitch == image.info.pitch;
|
||||
}
|
||||
|
||||
@@ -386,6 +408,9 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
|
||||
const auto descriptor_pitch =
|
||||
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
||||
descriptor.TileMode());
|
||||
if (ShaderFailureNonFatal()) {
|
||||
return;
|
||||
}
|
||||
EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d "
|
||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d "
|
||||
"guest_format=%u swizzle=0x%03x image_format=%d view_format=%d image_layers=%u "
|
||||
@@ -549,6 +574,9 @@ struct NullImageSpec {
|
||||
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
|
||||
const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint ||
|
||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
||||
if (resource.depth_compare && !uint_image) {
|
||||
return NullImageSpec {vk::Format::eD32Sfloat, Prospero::BufferFormat::k32Float};
|
||||
}
|
||||
return uint_image ? NullImageSpec {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt}
|
||||
: NullImageSpec {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
|
||||
}
|
||||
@@ -717,16 +745,10 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
(base_level != 0 || last_level == 0 || last_level > 3 || max_mip != last_level ||
|
||||
!msaa_tile || (descriptor.MsaaDepth() && !depth_tile) ||
|
||||
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
||||
EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u "
|
||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
||||
"dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
|
||||
base_level, last_level, levels, descriptor.MaxMip(),
|
||||
static_cast<uint32_t>(descriptor.Type()), static_cast<uint32_t>(tile),
|
||||
static_cast<uint32_t>(resource.kind), static_cast<uint32_t>(resource.dimension),
|
||||
static_cast<uint32_t>(resource.mip_mode), resource.read, resource.written,
|
||||
descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3],
|
||||
descriptor.fields[4], descriptor.fields[5], descriptor.fields[6],
|
||||
descriptor.fields[7]);
|
||||
auto desc = NullTextureDesc(resource, storage ? TextureCache::BindingType::Storage
|
||||
: TextureCache::BindingType::Texture);
|
||||
const auto id = texture_cache.FindImage(desc);
|
||||
return {id, nullptr, std::move(desc)};
|
||||
}
|
||||
const auto samples = multisampled ? 1u << last_level : 1u;
|
||||
const auto view_levels =
|
||||
@@ -783,9 +805,17 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
? storage_view_format
|
||||
: pixel_format;
|
||||
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format);
|
||||
auto depth_pixel_format = pixel_format;
|
||||
if (resource.depth_compare) {
|
||||
switch (pixel_format) {
|
||||
case vk::Format::eR32Sfloat: depth_pixel_format = vk::Format::eD32Sfloat; break;
|
||||
case vk::Format::eR16Unorm: depth_pixel_format = vk::Format::eD16Unorm; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
TextureCache::ImageDesc desc {};
|
||||
desc.info.data = {address, size.size};
|
||||
desc.info.pixel_format = pixel_format;
|
||||
desc.info.pixel_format = depth_pixel_format;
|
||||
desc.info.guest_format = format;
|
||||
desc.info.type = TextureBaseType(type);
|
||||
desc.info.extent = {width, height, volume ? depth : 1u};
|
||||
@@ -800,7 +830,8 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
} else {
|
||||
PopulateTextureMipLayout(desc.info);
|
||||
}
|
||||
desc.view_info = TextureViewInfo(resource, descriptor, view_format, shader_conversion, storage,
|
||||
const auto depth_view_format = resource.depth_compare ? depth_pixel_format : view_format;
|
||||
desc.view_info = TextureViewInfo(resource, descriptor, depth_view_format, shader_conversion, storage,
|
||||
view_levels, desc.info.resources.layers);
|
||||
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
||||
|
||||
@@ -814,9 +845,12 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
||||
if (storage) {
|
||||
EXIT("depth target cannot be bound as a storage image\n");
|
||||
}
|
||||
ValidateDepthTargetBinding(resource, descriptor, image, pixel_format, size.size);
|
||||
(void)SelectSampledDepthView(image->info.pixel_format, pixel_format,
|
||||
ValidateDepthTargetBinding(resource, descriptor, image, depth_pixel_format, size.size);
|
||||
(void)SelectSampledDepthView(image->info.pixel_format, depth_pixel_format,
|
||||
descriptor.DstSelXYZW());
|
||||
if (resource.depth_compare) {
|
||||
desc.view_info.format = image->info.pixel_format;
|
||||
}
|
||||
} else if (storage) {
|
||||
ValidateStorageColorView(image->info.pixel_format, view_format, descriptor.DstSelXYZW());
|
||||
} else {
|
||||
@@ -909,6 +943,9 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
auto binding = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
||||
BindImage(binding.image_id, binding.desc.type == TextureCache::BindingType::Storage);
|
||||
{
|
||||
static std::atomic<uint32_t> bind_log {0};
|
||||
}
|
||||
descriptors.images.push_back(binding);
|
||||
}
|
||||
descriptors.samplers.reserve(program.info.samplers.size());
|
||||
@@ -1012,6 +1049,9 @@ void RenderExecutor::RebindImages(PreparedBindings& prepared) {
|
||||
old_image->binding = {};
|
||||
}
|
||||
images[i] = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
||||
{
|
||||
static std::atomic<uint32_t> bind_log {0};
|
||||
}
|
||||
BindImage(images[i].image_id,
|
||||
images[i].desc.type == TextureCache::BindingType::Storage);
|
||||
}
|
||||
|
||||
@@ -141,7 +141,12 @@ struct PipelineCache::ProgramCache {
|
||||
}
|
||||
|
||||
const auto module = CompileProgram(device, params, input_info);
|
||||
EXIT_IF(module == nullptr || !input_info.stage);
|
||||
if (module == nullptr || !input_info.stage) {
|
||||
if (ShaderFailureNonFatal()) {
|
||||
return ShaderProgram {};
|
||||
}
|
||||
EXIT("shader program compilation produced no module\n");
|
||||
}
|
||||
uint64_t id = MixId(MixId(params.hash, static_cast<uint64_t>(stage)), permutations.size());
|
||||
if (id == 0) {
|
||||
id = 1;
|
||||
@@ -373,7 +378,8 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
std::span<const RenderColorInfo> colors, const RenderDepthInfo& depth,
|
||||
const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command,
|
||||
const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology,
|
||||
bool primitive_restart_enable, const ShaderProgram& vertex_program,
|
||||
bool primitive_restart_enable, uint32_t patch_control_points,
|
||||
const ShaderProgram& vertex_program,
|
||||
const ShaderProgram& pixel_program) {
|
||||
KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200);
|
||||
|
||||
@@ -453,6 +459,7 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
||||
static_params.negative_one_to_one = !clip_control.dx_clip_space;
|
||||
static_params.depth_clip_enable = clip_control.IsZClipEnabled();
|
||||
static_params.topology = topology;
|
||||
static_params.patch_control_points = patch_control_points;
|
||||
static_params.primitive_restart_enable = primitive_restart_enable;
|
||||
static_params.samples = attachment_samples;
|
||||
static_params.sample_shading_enable =
|
||||
|
||||
@@ -38,6 +38,7 @@ struct PipelineStaticParameters {
|
||||
bool depth_clip_enable = true;
|
||||
int scissor_ltrb[4] = {0};
|
||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||
uint32_t patch_control_points = 3;
|
||||
bool primitive_restart_enable = false;
|
||||
uint32_t samples = 1;
|
||||
bool sample_shading_enable = false;
|
||||
@@ -80,7 +81,8 @@ static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
|
||||
static_assert(alignof(PipelineStaticParameters) == 1);
|
||||
static_assert(sizeof(PipelineStaticParameters) ==
|
||||
sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) * 2 + sizeof(int[4]) +
|
||||
sizeof(vk::PrimitiveTopology) + sizeof(bool) + sizeof(uint32_t) +
|
||||
sizeof(vk::PrimitiveTopology) + sizeof(uint32_t) + sizeof(bool) +
|
||||
sizeof(uint32_t) +
|
||||
sizeof(bool) * 4 + sizeof(vk::CompareOp) + sizeof(bool) + sizeof(float) * 2 +
|
||||
sizeof(bool) + sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) +
|
||||
sizeof(uint32_t[RENDER_COLOR_ATTACHMENTS_MAX]) + sizeof(bool) * 3 +
|
||||
@@ -144,7 +146,8 @@ public:
|
||||
std::span<const RenderColorInfo> colors, const RenderDepthInfo& depth,
|
||||
const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command,
|
||||
const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology,
|
||||
bool primitive_restart_enable, const ShaderProgram& vertex_program,
|
||||
bool primitive_restart_enable, uint32_t patch_control_points,
|
||||
const ShaderProgram& vertex_program,
|
||||
const ShaderProgram& pixel_program);
|
||||
ComputePipeline& CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
||||
const ShaderProgram& compute_program);
|
||||
|
||||
@@ -913,7 +913,7 @@ void CreatePipelineInternal(
|
||||
pipeline_info.pInputAssemblyState = &input_assembly;
|
||||
vk::PipelineTessellationStateCreateInfo tessellation_state {};
|
||||
tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo;
|
||||
tessellation_state.patchControlPoints = 3;
|
||||
tessellation_state.patchControlPoints = static_params.patch_control_points;
|
||||
pipeline_info.pTessellationState = (rect_list ? &tessellation_state : nullptr);
|
||||
pipeline_info.pViewportState = &viewport_state;
|
||||
pipeline_info.pRasterizationState = &rasterizer;
|
||||
@@ -976,6 +976,8 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipe
|
||||
comp_shader_stage_info.pSpecializationInfo = nullptr;
|
||||
EXIT_IF(!input_info.stage);
|
||||
const auto wave_size = input_info.stage.program->wave_size;
|
||||
{
|
||||
}
|
||||
if (graphics.compute_subgroup_size_control_enabled &&
|
||||
wave_size >= graphics.min_subgroup_size && wave_size <= graphics.max_subgroup_size) {
|
||||
comp_subgroup_size.sType =
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
@@ -74,6 +75,56 @@ struct SubmitInfo {
|
||||
}
|
||||
};
|
||||
|
||||
struct GpuCheckpoint {
|
||||
uint32_t phase = 0; // 0 = command buffer begin, 1 = command buffer end
|
||||
uint32_t slot = 0;
|
||||
uint32_t debug_op = 0;
|
||||
uint64_t submit_id = 0;
|
||||
uint64_t submit_seq = 0;
|
||||
uint32_t arg0 = 0;
|
||||
uint32_t arg1 = 0;
|
||||
uint32_t arg2 = 0;
|
||||
uint32_t arg3 = 0;
|
||||
uint64_t arg4 = 0;
|
||||
};
|
||||
|
||||
void GpuCrashDiagnosticsDump(GraphicContext& graphics, const char* context_text);
|
||||
|
||||
namespace GraphicsWorkCounters {
|
||||
inline std::atomic<uint64_t> draws {0};
|
||||
inline std::atomic<uint64_t> dispatches {0};
|
||||
|
||||
inline constexpr size_t MaxTrackedTargets = 64;
|
||||
inline std::atomic<uint64_t> targets[MaxTrackedTargets] {};
|
||||
inline std::atomic<uint64_t> target_ids[MaxTrackedTargets] {};
|
||||
inline std::atomic<size_t> target_count {0};
|
||||
|
||||
inline std::atomic<uint64_t> present_index {0};
|
||||
|
||||
inline std::atomic<uint64_t> watch_seq {0};
|
||||
|
||||
inline constexpr uint64_t PackImageId(uint32_t index, uint32_t generation) {
|
||||
return (static_cast<uint64_t>(generation) << 32u) | index;
|
||||
}
|
||||
|
||||
inline void NoteTarget(uint64_t address, uint64_t packed_image_id = 0) {
|
||||
if (address == 0) {
|
||||
return;
|
||||
}
|
||||
const auto count = target_count.load(std::memory_order_relaxed);
|
||||
for (size_t i = 0; i < count && i < MaxTrackedTargets; i++) {
|
||||
if (targets[i].load(std::memory_order_relaxed) == address) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (count < MaxTrackedTargets) {
|
||||
targets[count].store(address, std::memory_order_relaxed);
|
||||
target_ids[count].store(packed_image_id, std::memory_order_relaxed);
|
||||
target_count.store(count + 1, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
} // namespace GraphicsWorkCounters
|
||||
|
||||
class CommandBuffer {
|
||||
public:
|
||||
~CommandBuffer() = default;
|
||||
@@ -115,6 +166,8 @@ private:
|
||||
uint32_t m_debug_arg2 = 0;
|
||||
uint32_t m_debug_arg3 = 0;
|
||||
uint64_t m_debug_arg4 = 0;
|
||||
mutable GpuCheckpoint* m_checkpoint_begin = nullptr;
|
||||
mutable GpuCheckpoint* m_checkpoint_end = nullptr;
|
||||
mutable RenderState m_render_state;
|
||||
mutable bool m_rendering = false;
|
||||
HW::Context* m_registers = nullptr;
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
#include "common/timer.h"
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include <cstdlib>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include "common/common.h"
|
||||
#include "common/emulatorConfig.h"
|
||||
#include "common/file.h"
|
||||
@@ -21,6 +27,7 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||
#include "graphics/shader/shader.h"
|
||||
#include "kernel/eventQueue.h"
|
||||
#include "kernel/memory.h"
|
||||
#include "kernel/pthread.h"
|
||||
#include "libs/errno.h"
|
||||
|
||||
@@ -167,6 +174,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
uint32_t thread_group_z, uint32_t mode) {
|
||||
EXIT_IF(buffer.IsInvalid());
|
||||
m_context.GetCommandScheduler().PopPendingOperations();
|
||||
GraphicsWorkCounters::dispatches.fetch_add(1, std::memory_order_relaxed);
|
||||
auto& ctx = buffer.GetRegisters();
|
||||
auto& sh_ctx = buffer.GetShaders();
|
||||
|
||||
@@ -174,6 +182,37 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
thread_group_x, thread_group_y, thread_group_z, mode,
|
||||
sh_ctx.GetCs().cs_regs.data_addr);
|
||||
|
||||
const auto cs_addr = sh_ctx.GetCs().cs_regs.data_addr;
|
||||
uint32_t cs_index = UINT32_MAX;
|
||||
constexpr uint32_t skip_groups_over = 0u;
|
||||
if (skip_groups_over != 0 && (thread_group_x > skip_groups_over ||
|
||||
thread_group_y > skip_groups_over ||
|
||||
thread_group_z > skip_groups_over)) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
{
|
||||
static std::mutex census_mutex;
|
||||
static std::vector<uint64_t> census;
|
||||
std::lock_guard<std::mutex> lock(census_mutex);
|
||||
const auto found = std::find(census.begin(), census.end(), cs_addr);
|
||||
cs_index = found != census.end() ? static_cast<uint32_t>(found - census.begin())
|
||||
: static_cast<uint32_t>(census.size());
|
||||
if (found == census.end() && census.size() < 64) {
|
||||
census.push_back(cs_addr);
|
||||
}
|
||||
}
|
||||
constexpr std::pair<uint32_t, uint32_t> skip_index_range {1u, 0u};
|
||||
if (cs_index >= skip_index_range.first && cs_index <= skip_index_range.second) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
constexpr uint64_t skip_cs_addr = 0ull;
|
||||
if (skip_cs_addr != 0 && sh_ctx.GetCs().cs_regs.data_addr == skip_cs_addr) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
|
||||
Common::LockGuard lock(m_context.GetMutex());
|
||||
if (sh_ctx.GetCs().cs_regs.data_addr == 0) {
|
||||
LOGF("GraphicsRenderDispatchDirect: temporary: ignoring dispatch with null CS shader, "
|
||||
@@ -207,6 +246,17 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
const auto& cs_regs = sh_ctx.GetCs();
|
||||
const auto& sh_regs = ctx.GetShaderRegisters();
|
||||
|
||||
{
|
||||
static std::mutex seen_mutex;
|
||||
static std::set<uint64_t> seen;
|
||||
const auto addr = cs_regs.cs_regs.data_addr;
|
||||
bool first = false;
|
||||
{
|
||||
std::lock_guard lock(seen_mutex);
|
||||
first = seen.insert(addr).second;
|
||||
}
|
||||
}
|
||||
|
||||
ShaderComputeInputInfo input_info {};
|
||||
const bool use_thread_dimensions = (mode & DISPATCH_INITIATOR_USE_THREAD_DIMENSIONS) != 0;
|
||||
input_info.dispatch_thread_dimensions = use_thread_dimensions;
|
||||
@@ -221,6 +271,10 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
const uint32_t frame_num = static_cast<uint32_t>(m_context.GetGpu().GetFrameNum());
|
||||
const bool large_workgroup =
|
||||
(input_info.threads_num[0] * input_info.threads_num[1] * input_info.threads_num[2] >= 512);
|
||||
if (!compute_program || !input_info.stage) {
|
||||
ResetBindings();
|
||||
return;
|
||||
}
|
||||
const auto& program = *input_info.stage.program;
|
||||
const auto& resources = *input_info.stage.resources;
|
||||
if (TryConsumeComputeMetaClear(input_info, buffer)) {
|
||||
@@ -239,23 +293,99 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
});
|
||||
const bool has_sampler = !program.info.samplers.empty();
|
||||
static std::atomic<uint32_t> dispatch_log_count {0};
|
||||
if ((large_workgroup || has_sampler) &&
|
||||
dispatch_log_count.fetch_add(1, std::memory_order_relaxed) < 512) {
|
||||
LOGF("GraphicsRenderDispatchDirect: frame=%u shader=0x%016" PRIx64
|
||||
" groups=%ux%ux%u mode=0x%08" PRIx32 " local=%ux%ux%u "
|
||||
"buffers=%zu textures=%zu sampled=%zu storage=%zu samplers=%zu push=%u\n",
|
||||
frame_num, sh_ctx.GetCs().cs_regs.data_addr, thread_group_x, thread_group_y,
|
||||
thread_group_z, mode, input_info.threads_num[0], input_info.threads_num[1],
|
||||
input_info.threads_num[2], program.info.buffers.size(), program.info.images.size(),
|
||||
sampled_images, program.info.images.size() - sampled_images,
|
||||
program.info.samplers.size(), program.bindings.push_constant_size);
|
||||
constexpr uint64_t traced_hash = 0ull;
|
||||
constexpr uint64_t writers_of = 0ull;
|
||||
if (writers_of != 0) {
|
||||
for (uint32_t b = 0; b < program.info.buffers.size(); b++) {
|
||||
const auto r = DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[b]);
|
||||
const auto base = r.Base48();
|
||||
const auto span = static_cast<uint64_t>(r.Stride()) * r.NumRecords();
|
||||
if (writers_of < base || writers_of >= base + std::max<uint64_t>(span, 1)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool force_trace = traced_hash != 0 && sh_ctx.GetCs().cs_regs.data_addr == traced_hash;
|
||||
if (force_trace || ((large_workgroup || has_sampler) &&
|
||||
dispatch_log_count.fetch_add(1, std::memory_order_relaxed) < 512)) {
|
||||
if (force_trace) {
|
||||
std::string words;
|
||||
for (uint32_t i = 0; i < resources.user_data.size() && i < 16; i++) {
|
||||
words += fmt::format("{}[{}]=0x{:08x}", i == 0 ? "" : " ", i,
|
||||
resources.user_data[i]);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||
const auto& buffer = program.info.buffers[i];
|
||||
const auto r = DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[i]);
|
||||
LOGF(" CS buffer[%u]: source=%u usage=%s addr=0x%012" PRIx64
|
||||
" stride=%u records=%u format=%u\n",
|
||||
i, buffer.source, buffer.written ? "read-write" : "read-only", r.Base48(),
|
||||
r.Stride(), r.NumRecords(), r.RawFormat());
|
||||
if (force_trace) {
|
||||
const auto span = static_cast<uint64_t>(r.Stride()) * r.NumRecords();
|
||||
if (span != 0 && span <= 2048) {
|
||||
std::array<uint8_t, 2048> raw {};
|
||||
if (Libs::LibKernel::Memory::TryReadBacking(r.Base48(), raw.data(), span)) {
|
||||
std::string bytes;
|
||||
for (uint64_t b = 0; b < span; b++) {
|
||||
bytes += fmt::format("{}{:02x}", b == 0 ? "" : " ", raw[b]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (force_trace && !buffer.written && r.Stride() == 8) {
|
||||
const auto mapped = Libs::LibKernel::Memory::MappedExtentFrom(r.Base48());
|
||||
constexpr uint64_t dump_at = 0ull;
|
||||
if (false && mapped > 0x200) {
|
||||
const auto* word = reinterpret_cast<const uint32_t*>(r.Base48());
|
||||
const auto node_end = std::min(mapped, uint64_t {0x180} + 0x1b9c0);
|
||||
std::vector<uint32_t> queue {6u};
|
||||
uint64_t visited = 0;
|
||||
uint64_t found = 0;
|
||||
uint32_t found_type = 0;
|
||||
uint32_t boxes = 0;
|
||||
uint32_t found_raw = 0;
|
||||
uint64_t parent = 0;
|
||||
while (!queue.empty() && visited < 4096 && found == 0) {
|
||||
const auto unit = queue.back();
|
||||
queue.pop_back();
|
||||
visited++;
|
||||
const uint64_t offset = static_cast<uint64_t>(unit) * 64;
|
||||
if (offset + 128 > node_end) {
|
||||
continue;
|
||||
}
|
||||
boxes++;
|
||||
const auto* node = word + offset / 4;
|
||||
for (uint32_t c = 0; c < 4; c++) {
|
||||
const auto ptr = node[c];
|
||||
const auto t = ptr & 7u;
|
||||
if (t == 7u || ptr == 0) {
|
||||
continue;
|
||||
}
|
||||
if (t == 5u) {
|
||||
queue.push_back(ptr >> 3u);
|
||||
} else {
|
||||
found = static_cast<uint64_t>(ptr >> 3u) * 64;
|
||||
found_type = t;
|
||||
found_raw = ptr;
|
||||
parent = offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found != 0 && found + 64 <= mapped) {
|
||||
const auto* box = reinterpret_cast<const uint32_t*>(r.Base48() + parent);
|
||||
for (uint64_t row = 0; row < 8; row++) {
|
||||
}
|
||||
const auto* tri = reinterpret_cast<const uint32_t*>(r.Base48() + found);
|
||||
for (uint64_t row = 0; row < 4; row++) {
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr uint64_t DumpBytes = 256;
|
||||
if (mapped >= dump_at + DumpBytes) {
|
||||
const auto* word = reinterpret_cast<const uint32_t*>(r.Base48() + dump_at);
|
||||
for (uint64_t row = 0; row < DumpBytes / 16; row++) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||
const auto& image = program.info.images[i];
|
||||
@@ -341,24 +471,53 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer,
|
||||
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline,
|
||||
std::span {&descriptor_stage, 1u});
|
||||
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
||||
has_storage_writes =
|
||||
std::any_of(program.info.images.begin(), program.info.images.end(),
|
||||
[](const auto& image) {
|
||||
return image.written &&
|
||||
(image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||
image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint);
|
||||
}) ||
|
||||
has_storage_writes;
|
||||
bool writes_storage_image = false;
|
||||
EXIT_IF(bindings.resources.images.size() != program.info.images.size());
|
||||
for (size_t i = 0; i < program.info.images.size(); i++) {
|
||||
const auto& image = program.info.images[i];
|
||||
if (image.written && (image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||
image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint)) {
|
||||
writes_storage_image = true;
|
||||
const auto addr = bindings.resources.images[i].desc.info.data.address;
|
||||
GraphicsWorkCounters::NoteTarget(
|
||||
addr, GraphicsWorkCounters::PackImageId(
|
||||
bindings.resources.images[i].image_id.index,
|
||||
bindings.resources.images[i].image_id.generation));
|
||||
constexpr uint64_t watch = 0ull;
|
||||
if (watch != 0 && addr == watch) {
|
||||
const auto& img = m_context.GetTextureCache().GetImage(
|
||||
bindings.resources.images[i].image_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
has_storage_writes = writes_storage_image || has_storage_writes;
|
||||
if (has_storage_writes) {
|
||||
// A host fence used to serialize every dispatch. Preserve its read-before-write ordering
|
||||
// while allowing the queue to execute asynchronously.
|
||||
ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
}
|
||||
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
||||
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
||||
constexpr uint64_t serialize_hash = 0ull;
|
||||
if (serialize_hash != 0 && sh_ctx.GetCs().cs_regs.data_addr == serialize_hash &&
|
||||
thread_group_x * thread_group_y * thread_group_z > 1) {
|
||||
for (uint32_t z = 0; z < thread_group_z; z++) {
|
||||
for (uint32_t y = 0; y < thread_group_y; y++) {
|
||||
for (uint32_t x = 0; x < thread_group_x; x++) {
|
||||
(void)x;
|
||||
(void)y;
|
||||
(void)z;
|
||||
vk_buffer.dispatch(1, 1, 1);
|
||||
ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z);
|
||||
}
|
||||
|
||||
// The removed host fence also ordered read-only dispatches before later writers.
|
||||
ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||
|
||||
ResetBindings();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
@@ -183,12 +184,27 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
|
||||
const auto& ctx = buffer.GetRegisters();
|
||||
const auto& ucfg = buffer.GetUserConfig();
|
||||
if (color.type == RenderColorType::NoColorOutput) {
|
||||
constexpr uint64_t no_color_from = 0ull;
|
||||
if (no_color_from != 0) {
|
||||
const auto present =
|
||||
GraphicsWorkCounters::present_index.load(std::memory_order_relaxed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto log_id = g_draw_state_log_count.fetch_add(1);
|
||||
if (log_id >= 192) {
|
||||
return;
|
||||
constexpr uint64_t log_from = 0ull;
|
||||
uint64_t log_id = 0;
|
||||
if (log_from != 0) {
|
||||
const auto present = GraphicsWorkCounters::present_index.load(std::memory_order_relaxed);
|
||||
if (present < log_from || present > log_from + 1) {
|
||||
return;
|
||||
}
|
||||
log_id = g_draw_state_log_count.fetch_add(1);
|
||||
} else {
|
||||
log_id = g_draw_state_log_count.fetch_add(1);
|
||||
if (log_id >= 192) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& cc = ctx.GetColorControl();
|
||||
@@ -430,6 +446,13 @@ static bool ShouldSkipGeShader(const CommandBuffer& buffer) {
|
||||
const auto& vertex_info = sh_ctx.GetVs();
|
||||
const auto stages = ctx.GetShaderStages();
|
||||
|
||||
if (ucfg.GetPrimType() == Prospero::PrimitiveType::kPatch) {
|
||||
static std::atomic<uint32_t> patch_log {0};
|
||||
if (patch_log.fetch_add(1, std::memory_order_relaxed) < 8) {
|
||||
const auto cfg = sh_regs.m_vgtLsHsConfig;
|
||||
}
|
||||
}
|
||||
|
||||
const auto is_known_gs_out_prim_type = [](uint32_t value) {
|
||||
switch (static_cast<Prospero::GsOutputPrimitiveType>(value)) {
|
||||
case Prospero::GsOutputPrimitiveType::kPoints:
|
||||
@@ -901,6 +924,13 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
||||
case Prospero::PrimitiveType::kRectList:
|
||||
topology = vk::PrimitiveTopology::ePatchList;
|
||||
break;
|
||||
case Prospero::PrimitiveType::kPatch:
|
||||
if (true) {
|
||||
EXIT("unknown primitive type: %u (kPatch needs tessellation)\n",
|
||||
static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
}
|
||||
topology = vk::PrimitiveTopology::ePatchList;
|
||||
break;
|
||||
case Prospero::PrimitiveType::kRectListLegacy:
|
||||
if (!auto_draw) {
|
||||
EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
|
||||
@@ -1025,6 +1055,10 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, bool
|
||||
state.pixel_program =
|
||||
pipeline_cache.GetPixelProgram(pixel_shader_info, shader_regs, state.vs_input_info,
|
||||
target_export_mapping, state.ps_input_info);
|
||||
if (!state.pixel_program && ShaderFailureNonFatal()) {
|
||||
static std::atomic<uint32_t> skip_log {0};
|
||||
state.ps_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
static PreparedVertexBuffers PrepareVertexBuffers(uint64_t submit_id, CommandBuffer& buffer,
|
||||
@@ -1097,7 +1131,13 @@ static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo
|
||||
return;
|
||||
}
|
||||
|
||||
if (!always_log && !force_legacy_rect_log) {
|
||||
bool window_log = false;
|
||||
constexpr uint64_t log_from = 0ull;
|
||||
if (log_from != 0) {
|
||||
const auto present = GraphicsWorkCounters::present_index.load(std::memory_order_relaxed);
|
||||
window_log = present >= log_from && present <= log_from + 1;
|
||||
}
|
||||
if (!always_log && !force_legacy_rect_log && !window_log) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1123,6 +1163,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
||||
case Prospero::PrimitiveType::kTriFan:
|
||||
case Prospero::PrimitiveType::kTriStrip:
|
||||
case Prospero::PrimitiveType::kRectList:
|
||||
case Prospero::PrimitiveType::kPatch:
|
||||
if (emit.indexed) {
|
||||
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
||||
draw.first_instance);
|
||||
@@ -1162,6 +1203,13 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
||||
bool primitive_restart_enable, bool log_pipeline_phase,
|
||||
bool set_bind_debug, bool set_auto_debug) {
|
||||
EXIT_IF(draw.name == nullptr);
|
||||
GraphicsWorkCounters::draws.fetch_add(1, std::memory_order_relaxed);
|
||||
for (uint32_t i = 0; i < state.color_count && i < RENDER_COLOR_ATTACHMENTS_MAX; i++) {
|
||||
GraphicsWorkCounters::NoteTarget(
|
||||
state.color_info[i].base_addr,
|
||||
GraphicsWorkCounters::PackImageId(state.color_info[i].image_id.index,
|
||||
state.color_info[i].image_id.generation));
|
||||
}
|
||||
auto& ucfg = buffer.GetUserConfig();
|
||||
|
||||
LogDrawPhase(draw.name, "PrepareBindings");
|
||||
@@ -1175,9 +1223,17 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
||||
if (log_pipeline_phase) {
|
||||
LogDrawPhase(draw.name, "CreatePipeline");
|
||||
}
|
||||
if (!state.vertex_program) {
|
||||
static std::atomic<uint32_t> skip_log {0};
|
||||
return;
|
||||
}
|
||||
auto& pipeline = m_context.GetPipelineCache().CreateGraphicsPipeline(
|
||||
std::span {state.color_info, state.color_count}, state.depth_info, state.vs_input_info, buffer,
|
||||
state.ps_active ? &state.ps_input_info : nullptr, topology, primitive_restart_enable,
|
||||
ucfg.GetPrimType() == Prospero::PrimitiveType::kPatch
|
||||
? std::max<uint32_t>(
|
||||
(buffer.GetRegisters().GetShaderRegisters().m_vgtLsHsConfig >> 8u) & 0x3fu, 1u)
|
||||
: 3u,
|
||||
state.vertex_program, state.pixel_program);
|
||||
|
||||
// Resource preparation above may synchronously finish and restart the scheduler. From this
|
||||
|
||||
Reference in New Issue
Block a user