mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-22 03:46:53 +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 "graphics/host_gpu/renderer/pipeline/descriptors.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
@@ -57,6 +58,7 @@ bool IsSampledImage(BindingKind kind) {
|
|||||||
case BindingKind::Sampled2DMsaa:
|
case BindingKind::Sampled2DMsaa:
|
||||||
case BindingKind::Sampled2DMsaaArray:
|
case BindingKind::Sampled2DMsaaArray:
|
||||||
case BindingKind::Sampled3D:
|
case BindingKind::Sampled3D:
|
||||||
|
case BindingKind::SampledDepth2D:
|
||||||
case BindingKind::SampledUint1D:
|
case BindingKind::SampledUint1D:
|
||||||
case BindingKind::SampledUint1DArray:
|
case BindingKind::SampledUint1DArray:
|
||||||
case BindingKind::SampledUint2D:
|
case BindingKind::SampledUint2D:
|
||||||
@@ -186,14 +188,28 @@ static BufferView NativeStorageBuffer(RenderContext&
|
|||||||
BindNullStorageBuffer(context, result);
|
BindNullStorageBuffer(context, result);
|
||||||
return 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& graphics = context.GetGraphics();
|
||||||
const auto alignment = graphics.StorageMinAlignment();
|
const auto alignment = graphics.StorageMinAlignment();
|
||||||
if (alignment == 0 ||
|
if (alignment == 0) {
|
||||||
size > graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange) {
|
EXIT("storage buffer device alignment is unsupported\n");
|
||||||
EXIT("storage buffer range or 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);
|
resource.formatted, id);
|
||||||
const auto aligned_offset = offset - offset % alignment;
|
const auto aligned_offset = offset - offset % alignment;
|
||||||
const auto adjustment = offset - aligned_offset;
|
const auto adjustment = offset - aligned_offset;
|
||||||
@@ -204,7 +220,11 @@ static BufferView NativeStorageBuffer(RenderContext&
|
|||||||
buffer_offset = static_cast<uint32_t>(adjustment);
|
buffer_offset = static_cast<uint32_t>(adjustment);
|
||||||
result.buffer = buffer->Handle();
|
result.buffer = buffer->Handle();
|
||||||
result.offset = aligned_offset;
|
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) {
|
if (resource.formatted && resource.written) {
|
||||||
context.GetTextureCache().InvalidateMemoryFromGPU(address, size);
|
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_2d || supported_array || supported_cube || supported_msaa_2d ||
|
||||||
supported_msaa_array) &&
|
supported_msaa_array) &&
|
||||||
levels_ok && descriptor.MinLod() == 0 &&
|
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;
|
(!descriptor.MsaaDepth() || multisampled) && pitch >= width && pitch == image.info.pitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,6 +408,9 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource
|
|||||||
const auto descriptor_pitch =
|
const auto descriptor_pitch =
|
||||||
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
TileGetTexturePitch(descriptor.Format(), static_cast<uint32_t>(descriptor.Width5()) + 1u,
|
||||||
descriptor.TileMode());
|
descriptor.TileMode());
|
||||||
|
if (ShaderFailureNonFatal()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d "
|
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 "
|
"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 "
|
"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) {
|
static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) {
|
||||||
const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint ||
|
const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint ||
|
||||||
resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint;
|
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}
|
return uint_image ? NullImageSpec {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt}
|
||||||
: NullImageSpec {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float};
|
: 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 ||
|
(base_level != 0 || last_level == 0 || last_level > 3 || max_mip != last_level ||
|
||||||
!msaa_tile || (descriptor.MsaaDepth() && !depth_tile) ||
|
!msaa_tile || (descriptor.MsaaDepth() && !depth_tile) ||
|
||||||
(!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) {
|
(!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 "
|
auto desc = NullTextureDesc(resource, storage ? TextureCache::BindingType::Storage
|
||||||
"kind=%u dimension=%u mip_mode=%u read=%d written=%d "
|
: TextureCache::BindingType::Texture);
|
||||||
"dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n",
|
const auto id = texture_cache.FindImage(desc);
|
||||||
base_level, last_level, levels, descriptor.MaxMip(),
|
return {id, nullptr, std::move(desc)};
|
||||||
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]);
|
|
||||||
}
|
}
|
||||||
const auto samples = multisampled ? 1u << last_level : 1u;
|
const auto samples = multisampled ? 1u << last_level : 1u;
|
||||||
const auto view_levels =
|
const auto view_levels =
|
||||||
@@ -783,9 +805,17 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
|||||||
? storage_view_format
|
? storage_view_format
|
||||||
: pixel_format;
|
: pixel_format;
|
||||||
const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(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 {};
|
TextureCache::ImageDesc desc {};
|
||||||
desc.info.data = {address, size.size};
|
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.guest_format = format;
|
||||||
desc.info.type = TextureBaseType(type);
|
desc.info.type = TextureBaseType(type);
|
||||||
desc.info.extent = {width, height, volume ? depth : 1u};
|
desc.info.extent = {width, height, volume ? depth : 1u};
|
||||||
@@ -800,7 +830,8 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
|||||||
} else {
|
} else {
|
||||||
PopulateTextureMipLayout(desc.info);
|
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);
|
view_levels, desc.info.resources.layers);
|
||||||
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture;
|
||||||
|
|
||||||
@@ -814,9 +845,12 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR
|
|||||||
if (storage) {
|
if (storage) {
|
||||||
EXIT("depth target cannot be bound as a storage image\n");
|
EXIT("depth target cannot be bound as a storage image\n");
|
||||||
}
|
}
|
||||||
ValidateDepthTargetBinding(resource, descriptor, image, pixel_format, size.size);
|
ValidateDepthTargetBinding(resource, descriptor, image, depth_pixel_format, size.size);
|
||||||
(void)SelectSampledDepthView(image->info.pixel_format, pixel_format,
|
(void)SelectSampledDepthView(image->info.pixel_format, depth_pixel_format,
|
||||||
descriptor.DstSelXYZW());
|
descriptor.DstSelXYZW());
|
||||||
|
if (resource.depth_compare) {
|
||||||
|
desc.view_info.format = image->info.pixel_format;
|
||||||
|
}
|
||||||
} else if (storage) {
|
} else if (storage) {
|
||||||
ValidateStorageColorView(image->info.pixel_format, view_format, descriptor.DstSelXYZW());
|
ValidateStorageColorView(image->info.pixel_format, view_format, descriptor.DstSelXYZW());
|
||||||
} else {
|
} else {
|
||||||
@@ -909,6 +943,9 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti
|
|||||||
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||||
auto binding = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
auto binding = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
||||||
BindImage(binding.image_id, binding.desc.type == TextureCache::BindingType::Storage);
|
BindImage(binding.image_id, binding.desc.type == TextureCache::BindingType::Storage);
|
||||||
|
{
|
||||||
|
static std::atomic<uint32_t> bind_log {0};
|
||||||
|
}
|
||||||
descriptors.images.push_back(binding);
|
descriptors.images.push_back(binding);
|
||||||
}
|
}
|
||||||
descriptors.samplers.reserve(program.info.samplers.size());
|
descriptors.samplers.reserve(program.info.samplers.size());
|
||||||
@@ -1012,6 +1049,9 @@ void RenderExecutor::RebindImages(PreparedBindings& prepared) {
|
|||||||
old_image->binding = {};
|
old_image->binding = {};
|
||||||
}
|
}
|
||||||
images[i] = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
images[i] = ResolveTexture(program.info.images[i], snapshot.images[i]);
|
||||||
|
{
|
||||||
|
static std::atomic<uint32_t> bind_log {0};
|
||||||
|
}
|
||||||
BindImage(images[i].image_id,
|
BindImage(images[i].image_id,
|
||||||
images[i].desc.type == TextureCache::BindingType::Storage);
|
images[i].desc.type == TextureCache::BindingType::Storage);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,7 +141,12 @@ struct PipelineCache::ProgramCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto module = CompileProgram(device, params, input_info);
|
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());
|
uint64_t id = MixId(MixId(params.hash, static_cast<uint64_t>(stage)), permutations.size());
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
id = 1;
|
id = 1;
|
||||||
@@ -373,7 +378,8 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline(
|
|||||||
std::span<const RenderColorInfo> colors, const RenderDepthInfo& depth,
|
std::span<const RenderColorInfo> colors, const RenderDepthInfo& depth,
|
||||||
const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command,
|
const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command,
|
||||||
const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology,
|
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) {
|
const ShaderProgram& pixel_program) {
|
||||||
KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200);
|
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.negative_one_to_one = !clip_control.dx_clip_space;
|
||||||
static_params.depth_clip_enable = clip_control.IsZClipEnabled();
|
static_params.depth_clip_enable = clip_control.IsZClipEnabled();
|
||||||
static_params.topology = topology;
|
static_params.topology = topology;
|
||||||
|
static_params.patch_control_points = patch_control_points;
|
||||||
static_params.primitive_restart_enable = primitive_restart_enable;
|
static_params.primitive_restart_enable = primitive_restart_enable;
|
||||||
static_params.samples = attachment_samples;
|
static_params.samples = attachment_samples;
|
||||||
static_params.sample_shading_enable =
|
static_params.sample_shading_enable =
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ struct PipelineStaticParameters {
|
|||||||
bool depth_clip_enable = true;
|
bool depth_clip_enable = true;
|
||||||
int scissor_ltrb[4] = {0};
|
int scissor_ltrb[4] = {0};
|
||||||
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
|
||||||
|
uint32_t patch_control_points = 3;
|
||||||
bool primitive_restart_enable = false;
|
bool primitive_restart_enable = false;
|
||||||
uint32_t samples = 1;
|
uint32_t samples = 1;
|
||||||
bool sample_shading_enable = false;
|
bool sample_shading_enable = false;
|
||||||
@@ -80,7 +81,8 @@ static_assert(std::is_standard_layout_v<PipelineStaticParameters>);
|
|||||||
static_assert(alignof(PipelineStaticParameters) == 1);
|
static_assert(alignof(PipelineStaticParameters) == 1);
|
||||||
static_assert(sizeof(PipelineStaticParameters) ==
|
static_assert(sizeof(PipelineStaticParameters) ==
|
||||||
sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) * 2 + sizeof(int[4]) +
|
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) * 4 + sizeof(vk::CompareOp) + sizeof(bool) + sizeof(float) * 2 +
|
||||||
sizeof(bool) + sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) +
|
sizeof(bool) + sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) +
|
||||||
sizeof(uint32_t[RENDER_COLOR_ATTACHMENTS_MAX]) + sizeof(bool) * 3 +
|
sizeof(uint32_t[RENDER_COLOR_ATTACHMENTS_MAX]) + sizeof(bool) * 3 +
|
||||||
@@ -144,7 +146,8 @@ public:
|
|||||||
std::span<const RenderColorInfo> colors, const RenderDepthInfo& depth,
|
std::span<const RenderColorInfo> colors, const RenderDepthInfo& depth,
|
||||||
const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command,
|
const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command,
|
||||||
const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology,
|
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);
|
const ShaderProgram& pixel_program);
|
||||||
ComputePipeline& CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
ComputePipeline& CreateComputePipeline(ShaderComputeInputInfo& input_info,
|
||||||
const ShaderProgram& compute_program);
|
const ShaderProgram& compute_program);
|
||||||
|
|||||||
@@ -913,7 +913,7 @@ void CreatePipelineInternal(
|
|||||||
pipeline_info.pInputAssemblyState = &input_assembly;
|
pipeline_info.pInputAssemblyState = &input_assembly;
|
||||||
vk::PipelineTessellationStateCreateInfo tessellation_state {};
|
vk::PipelineTessellationStateCreateInfo tessellation_state {};
|
||||||
tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo;
|
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.pTessellationState = (rect_list ? &tessellation_state : nullptr);
|
||||||
pipeline_info.pViewportState = &viewport_state;
|
pipeline_info.pViewportState = &viewport_state;
|
||||||
pipeline_info.pRasterizationState = &rasterizer;
|
pipeline_info.pRasterizationState = &rasterizer;
|
||||||
@@ -976,6 +976,8 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipe
|
|||||||
comp_shader_stage_info.pSpecializationInfo = nullptr;
|
comp_shader_stage_info.pSpecializationInfo = nullptr;
|
||||||
EXIT_IF(!input_info.stage);
|
EXIT_IF(!input_info.stage);
|
||||||
const auto wave_size = input_info.stage.program->wave_size;
|
const auto wave_size = input_info.stage.program->wave_size;
|
||||||
|
{
|
||||||
|
}
|
||||||
if (graphics.compute_subgroup_size_control_enabled &&
|
if (graphics.compute_subgroup_size_control_enabled &&
|
||||||
wave_size >= graphics.min_subgroup_size && wave_size <= graphics.max_subgroup_size) {
|
wave_size >= graphics.min_subgroup_size && wave_size <= graphics.max_subgroup_size) {
|
||||||
comp_subgroup_size.sType =
|
comp_subgroup_size.sType =
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <vector>
|
#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 {
|
class CommandBuffer {
|
||||||
public:
|
public:
|
||||||
~CommandBuffer() = default;
|
~CommandBuffer() = default;
|
||||||
@@ -115,6 +166,8 @@ private:
|
|||||||
uint32_t m_debug_arg2 = 0;
|
uint32_t m_debug_arg2 = 0;
|
||||||
uint32_t m_debug_arg3 = 0;
|
uint32_t m_debug_arg3 = 0;
|
||||||
uint64_t m_debug_arg4 = 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 RenderState m_render_state;
|
||||||
mutable bool m_rendering = false;
|
mutable bool m_rendering = false;
|
||||||
HW::Context* m_registers = nullptr;
|
HW::Context* m_registers = nullptr;
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
|
#include "common/timer.h"
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <mutex>
|
||||||
|
#include <set>
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
#include "common/file.h"
|
#include "common/file.h"
|
||||||
@@ -21,6 +27,7 @@
|
|||||||
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
#include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h"
|
||||||
#include "graphics/shader/shader.h"
|
#include "graphics/shader/shader.h"
|
||||||
#include "kernel/eventQueue.h"
|
#include "kernel/eventQueue.h"
|
||||||
|
#include "kernel/memory.h"
|
||||||
#include "kernel/pthread.h"
|
#include "kernel/pthread.h"
|
||||||
#include "libs/errno.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) {
|
uint32_t thread_group_z, uint32_t mode) {
|
||||||
EXIT_IF(buffer.IsInvalid());
|
EXIT_IF(buffer.IsInvalid());
|
||||||
m_context.GetCommandScheduler().PopPendingOperations();
|
m_context.GetCommandScheduler().PopPendingOperations();
|
||||||
|
GraphicsWorkCounters::dispatches.fetch_add(1, std::memory_order_relaxed);
|
||||||
auto& ctx = buffer.GetRegisters();
|
auto& ctx = buffer.GetRegisters();
|
||||||
auto& sh_ctx = buffer.GetShaders();
|
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,
|
thread_group_x, thread_group_y, thread_group_z, mode,
|
||||||
sh_ctx.GetCs().cs_regs.data_addr);
|
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());
|
Common::LockGuard lock(m_context.GetMutex());
|
||||||
if (sh_ctx.GetCs().cs_regs.data_addr == 0) {
|
if (sh_ctx.GetCs().cs_regs.data_addr == 0) {
|
||||||
LOGF("GraphicsRenderDispatchDirect: temporary: ignoring dispatch with null CS shader, "
|
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& cs_regs = sh_ctx.GetCs();
|
||||||
const auto& sh_regs = ctx.GetShaderRegisters();
|
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 {};
|
ShaderComputeInputInfo input_info {};
|
||||||
const bool use_thread_dimensions = (mode & DISPATCH_INITIATOR_USE_THREAD_DIMENSIONS) != 0;
|
const bool use_thread_dimensions = (mode & DISPATCH_INITIATOR_USE_THREAD_DIMENSIONS) != 0;
|
||||||
input_info.dispatch_thread_dimensions = use_thread_dimensions;
|
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 uint32_t frame_num = static_cast<uint32_t>(m_context.GetGpu().GetFrameNum());
|
||||||
const bool large_workgroup =
|
const bool large_workgroup =
|
||||||
(input_info.threads_num[0] * input_info.threads_num[1] * input_info.threads_num[2] >= 512);
|
(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& program = *input_info.stage.program;
|
||||||
const auto& resources = *input_info.stage.resources;
|
const auto& resources = *input_info.stage.resources;
|
||||||
if (TryConsumeComputeMetaClear(input_info, buffer)) {
|
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();
|
const bool has_sampler = !program.info.samplers.empty();
|
||||||
static std::atomic<uint32_t> dispatch_log_count {0};
|
static std::atomic<uint32_t> dispatch_log_count {0};
|
||||||
if ((large_workgroup || has_sampler) &&
|
constexpr uint64_t traced_hash = 0ull;
|
||||||
dispatch_log_count.fetch_add(1, std::memory_order_relaxed) < 512) {
|
constexpr uint64_t writers_of = 0ull;
|
||||||
LOGF("GraphicsRenderDispatchDirect: frame=%u shader=0x%016" PRIx64
|
if (writers_of != 0) {
|
||||||
" groups=%ux%ux%u mode=0x%08" PRIx32 " local=%ux%ux%u "
|
for (uint32_t b = 0; b < program.info.buffers.size(); b++) {
|
||||||
"buffers=%zu textures=%zu sampled=%zu storage=%zu samplers=%zu push=%u\n",
|
const auto r = DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[b]);
|
||||||
frame_num, sh_ctx.GetCs().cs_regs.data_addr, thread_group_x, thread_group_y,
|
const auto base = r.Base48();
|
||||||
thread_group_z, mode, input_info.threads_num[0], input_info.threads_num[1],
|
const auto span = static_cast<uint64_t>(r.Stride()) * r.NumRecords();
|
||||||
input_info.threads_num[2], program.info.buffers.size(), program.info.images.size(),
|
if (writers_of < base || writers_of >= base + std::max<uint64_t>(span, 1)) {
|
||||||
sampled_images, program.info.images.size() - sampled_images,
|
continue;
|
||||||
program.info.samplers.size(), program.bindings.push_constant_size);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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++) {
|
for (uint32_t i = 0; i < program.info.buffers.size(); i++) {
|
||||||
const auto& buffer = program.info.buffers[i];
|
const auto& buffer = program.info.buffers[i];
|
||||||
const auto r = DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[i]);
|
const auto r = DecodeNativeDescriptor<ShaderBufferResource>(resources.buffers[i]);
|
||||||
LOGF(" CS buffer[%u]: source=%u usage=%s addr=0x%012" PRIx64
|
if (force_trace) {
|
||||||
" stride=%u records=%u format=%u\n",
|
const auto span = static_cast<uint64_t>(r.Stride()) * r.NumRecords();
|
||||||
i, buffer.source, buffer.written ? "read-write" : "read-only", r.Base48(),
|
if (span != 0 && span <= 2048) {
|
||||||
r.Stride(), r.NumRecords(), r.RawFormat());
|
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++) {
|
for (uint32_t i = 0; i < program.info.images.size(); i++) {
|
||||||
const auto& image = program.info.images[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,
|
CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline,
|
||||||
std::span {&descriptor_stage, 1u});
|
std::span {&descriptor_stage, 1u});
|
||||||
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
bool has_storage_writes = HasShaderBufferWrites(input_info.stage);
|
||||||
has_storage_writes =
|
bool writes_storage_image = false;
|
||||||
std::any_of(program.info.images.begin(), program.info.images.end(),
|
EXIT_IF(bindings.resources.images.size() != program.info.images.size());
|
||||||
[](const auto& image) {
|
for (size_t i = 0; i < program.info.images.size(); i++) {
|
||||||
return image.written &&
|
const auto& image = program.info.images[i];
|
||||||
(image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
if (image.written && (image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage ||
|
||||||
image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint);
|
image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint)) {
|
||||||
}) ||
|
writes_storage_image = true;
|
||||||
has_storage_writes;
|
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) {
|
if (has_storage_writes) {
|
||||||
// A host fence used to serialize every dispatch. Preserve its read-before-write ordering
|
// A host fence used to serialize every dispatch. Preserve its read-before-write ordering
|
||||||
// while allowing the queue to execute asynchronously.
|
// while allowing the queue to execute asynchronously.
|
||||||
ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||||
}
|
}
|
||||||
vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline);
|
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.
|
// The removed host fence also ordered read-only dispatches before later writers.
|
||||||
ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader);
|
||||||
|
|
||||||
ResetBindings();
|
ResetBindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include <bit>
|
#include <bit>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -183,12 +184,27 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col
|
|||||||
const auto& ctx = buffer.GetRegisters();
|
const auto& ctx = buffer.GetRegisters();
|
||||||
const auto& ucfg = buffer.GetUserConfig();
|
const auto& ucfg = buffer.GetUserConfig();
|
||||||
if (color.type == RenderColorType::NoColorOutput) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto log_id = g_draw_state_log_count.fetch_add(1);
|
constexpr uint64_t log_from = 0ull;
|
||||||
if (log_id >= 192) {
|
uint64_t log_id = 0;
|
||||||
return;
|
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();
|
const auto& cc = ctx.GetColorControl();
|
||||||
@@ -430,6 +446,13 @@ static bool ShouldSkipGeShader(const CommandBuffer& buffer) {
|
|||||||
const auto& vertex_info = sh_ctx.GetVs();
|
const auto& vertex_info = sh_ctx.GetVs();
|
||||||
const auto stages = ctx.GetShaderStages();
|
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) {
|
const auto is_known_gs_out_prim_type = [](uint32_t value) {
|
||||||
switch (static_cast<Prospero::GsOutputPrimitiveType>(value)) {
|
switch (static_cast<Prospero::GsOutputPrimitiveType>(value)) {
|
||||||
case Prospero::GsOutputPrimitiveType::kPoints:
|
case Prospero::GsOutputPrimitiveType::kPoints:
|
||||||
@@ -901,6 +924,13 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw,
|
|||||||
case Prospero::PrimitiveType::kRectList:
|
case Prospero::PrimitiveType::kRectList:
|
||||||
topology = vk::PrimitiveTopology::ePatchList;
|
topology = vk::PrimitiveTopology::ePatchList;
|
||||||
break;
|
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:
|
case Prospero::PrimitiveType::kRectListLegacy:
|
||||||
if (!auto_draw) {
|
if (!auto_draw) {
|
||||||
EXIT("unknown primitive type: %u\n", static_cast<uint32_t>(ucfg.GetPrimType()));
|
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 =
|
state.pixel_program =
|
||||||
pipeline_cache.GetPixelProgram(pixel_shader_info, shader_regs, state.vs_input_info,
|
pipeline_cache.GetPixelProgram(pixel_shader_info, shader_regs, state.vs_input_info,
|
||||||
target_export_mapping, state.ps_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,
|
static PreparedVertexBuffers PrepareVertexBuffers(uint64_t submit_id, CommandBuffer& buffer,
|
||||||
@@ -1097,7 +1131,13 @@ static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo
|
|||||||
return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1123,6 +1163,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_
|
|||||||
case Prospero::PrimitiveType::kTriFan:
|
case Prospero::PrimitiveType::kTriFan:
|
||||||
case Prospero::PrimitiveType::kTriStrip:
|
case Prospero::PrimitiveType::kTriStrip:
|
||||||
case Prospero::PrimitiveType::kRectList:
|
case Prospero::PrimitiveType::kRectList:
|
||||||
|
case Prospero::PrimitiveType::kPatch:
|
||||||
if (emit.indexed) {
|
if (emit.indexed) {
|
||||||
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset,
|
||||||
draw.first_instance);
|
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 primitive_restart_enable, bool log_pipeline_phase,
|
||||||
bool set_bind_debug, bool set_auto_debug) {
|
bool set_bind_debug, bool set_auto_debug) {
|
||||||
EXIT_IF(draw.name == nullptr);
|
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();
|
auto& ucfg = buffer.GetUserConfig();
|
||||||
|
|
||||||
LogDrawPhase(draw.name, "PrepareBindings");
|
LogDrawPhase(draw.name, "PrepareBindings");
|
||||||
@@ -1175,9 +1223,17 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
|||||||
if (log_pipeline_phase) {
|
if (log_pipeline_phase) {
|
||||||
LogDrawPhase(draw.name, "CreatePipeline");
|
LogDrawPhase(draw.name, "CreatePipeline");
|
||||||
}
|
}
|
||||||
|
if (!state.vertex_program) {
|
||||||
|
static std::atomic<uint32_t> skip_log {0};
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto& pipeline = m_context.GetPipelineCache().CreateGraphicsPipeline(
|
auto& pipeline = m_context.GetPipelineCache().CreateGraphicsPipeline(
|
||||||
std::span {state.color_info, state.color_count}, state.depth_info, state.vs_input_info, buffer,
|
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,
|
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);
|
state.vertex_program, state.pixel_program);
|
||||||
|
|
||||||
// Resource preparation above may synchronously finish and restart the scheduler. From this
|
// Resource preparation above may synchronously finish and restart the scheduler. From this
|
||||||
|
|||||||
Reference in New Issue
Block a user