mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-19 18:36:53 +02:00
render: support overlapping fragment depth feedback
This commit is contained in:
@@ -32,6 +32,7 @@ struct GraphicContext {
|
||||
bool rt_extensions_enabled = false;
|
||||
bool compute_subgroup_size_control_enabled = false;
|
||||
bool sample_rate_shading_enabled = false;
|
||||
bool attachment_feedback_loop_enabled = false;
|
||||
bool mesh_shader_enabled = false;
|
||||
vk::PhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
uint32_t subgroup_size = 0;
|
||||
|
||||
@@ -54,6 +54,9 @@ namespace {
|
||||
}
|
||||
if (DepthAspectTransferFormat(info.pixel_format) != vk::Format::eUndefined) {
|
||||
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
if (graphics.attachment_feedback_loop_enabled && (usage & vk::ImageUsageFlagBits::eSampled)) {
|
||||
usage |= vk::ImageUsageFlagBits::eAttachmentFeedbackLoopEXT;
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eColorAttachment)) {
|
||||
|
||||
@@ -1004,8 +1004,11 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer,
|
||||
std::ranges::find(image.views, binding.image_view, &CachedImageView::view);
|
||||
EXIT_IF(storage || host_view == image.views.end());
|
||||
const auto aspect = host_view->info.aspect;
|
||||
const bool depth_feedback =
|
||||
layout == vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT &&
|
||||
program.stage == ShaderType::Pixel;
|
||||
const bool depth_read =
|
||||
layout == vk::ImageLayout::eDepthReadOnlyOptimal ||
|
||||
depth_feedback || layout == vk::ImageLayout::eDepthReadOnlyOptimal ||
|
||||
layout == vk::ImageLayout::eDepthStencilReadOnlyOptimal ||
|
||||
layout == vk::ImageLayout::eDepthReadOnlyStencilAttachmentOptimal;
|
||||
const bool stencil_read =
|
||||
|
||||
@@ -702,7 +702,7 @@ void CreatePipelineInternal(
|
||||
depth_stencil_info.minDepthBounds = static_params.depth_min_bounds;
|
||||
depth_stencil_info.maxDepthBounds = static_params.depth_max_bounds;
|
||||
|
||||
const vk::DynamicState dynamic_states[] = {
|
||||
std::vector<vk::DynamicState> dynamic_states {
|
||||
vk::DynamicState::eViewportWithCount,
|
||||
vk::DynamicState::eScissorWithCount,
|
||||
vk::DynamicState::eLineWidth,
|
||||
@@ -715,22 +715,19 @@ void CreatePipelineInternal(
|
||||
vk::DynamicState::eStencilReference,
|
||||
vk::DynamicState::eStencilWriteMask,
|
||||
vk::DynamicState::eBlendConstants,
|
||||
#if !defined(__APPLE__)
|
||||
// Keep last so depth-only pipelines can omit this dynamic state.
|
||||
vk::DynamicState::eColorWriteEnableEXT, // unsupported by MoltenVK; static mask instead
|
||||
#endif
|
||||
};
|
||||
auto dynamic_states_count =
|
||||
static_cast<uint32_t>(sizeof(dynamic_states) / sizeof(dynamic_states[0]));
|
||||
#if !defined(__APPLE__)
|
||||
if (rendering.color_count == 0) {
|
||||
dynamic_states_count--;
|
||||
if (rendering.color_count != 0) {
|
||||
dynamic_states.push_back(vk::DynamicState::eColorWriteEnableEXT);
|
||||
}
|
||||
#endif
|
||||
if (graphics.attachment_feedback_loop_enabled) {
|
||||
dynamic_states.push_back(vk::DynamicState::eAttachmentFeedbackLoopEnableEXT);
|
||||
}
|
||||
|
||||
vk::PipelineDynamicStateCreateInfo dynamic_state {};
|
||||
dynamic_state.dynamicStateCount = dynamic_states_count;
|
||||
dynamic_state.pDynamicStates = dynamic_states;
|
||||
dynamic_state.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
|
||||
dynamic_state.pDynamicStates = dynamic_states.data();
|
||||
|
||||
vk::GraphicsPipelineCreateInfo pipeline_info {};
|
||||
vk::PipelineRenderingCreateInfo rendering_info {};
|
||||
@@ -764,7 +761,7 @@ void CreatePipelineInternal(
|
||||
vertex_program.id, ps_active ? pixel_program.id : 0,
|
||||
static_cast<uint32_t>(static_params.topology), static_params.color_mask[0],
|
||||
(with_depth ? "true" : "false"), (static_params.blend_enable[0] ? "true" : "false"),
|
||||
dynamic_states_count);
|
||||
dynamic_state.dynamicStateCount);
|
||||
}
|
||||
result = graphics.device.createGraphicsPipelines(driver_cache, 1, &pipeline_info, nullptr,
|
||||
&pipeline.pipeline);
|
||||
|
||||
@@ -195,7 +195,8 @@ private:
|
||||
bool primitive_restart_enable, bool log_pipeline_phase,
|
||||
bool set_bind_debug, bool set_auto_debug);
|
||||
[[nodiscard]] RenderState AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
|
||||
uint32_t color_count, RenderDepthInfo& depth);
|
||||
uint32_t color_count, RenderDepthInfo& depth,
|
||||
const std::optional<PreparedBindings>& pixel = std::nullopt);
|
||||
[[nodiscard]] bool ResolveColorTargets(uint64_t submit_id, CommandBuffer& buffer,
|
||||
uint32_t render_target_slice_offset);
|
||||
void BindImage(ImageId id, bool storage);
|
||||
|
||||
@@ -475,7 +475,8 @@ struct DrawCallInfo {
|
||||
};
|
||||
|
||||
RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderColorInfo* colors,
|
||||
uint32_t color_count, RenderDepthInfo& depth) {
|
||||
uint32_t color_count, RenderDepthInfo& depth,
|
||||
const std::optional<PreparedBindings>& pixel) {
|
||||
EXIT_IF(colors == nullptr || color_count > RENDER_COLOR_ATTACHMENTS_MAX);
|
||||
auto& cache = m_context.GetTextureCache();
|
||||
RenderState state {};
|
||||
@@ -567,7 +568,28 @@ RenderState RenderExecutor::AcquireRenderTargets(CommandBuffer& buffer, RenderCo
|
||||
EXIT("mixed color/depth sample counts are unsupported: %u and %u\n", attachment_samples,
|
||||
depth.desc.info.samples);
|
||||
}
|
||||
const auto layout = depth_attachment_layout(depth);
|
||||
const bool feedback = depth.depth_write_enable && pixel &&
|
||||
std::ranges::any_of(pixel->images, [&](const TextureBinding& binding) {
|
||||
if (binding.image_id != depth.image_id ||
|
||||
binding.desc.type != TextureCache::BindingType::Texture) {
|
||||
return false;
|
||||
}
|
||||
const auto native =
|
||||
std::ranges::find(image.views, binding.image_view, &CachedImageView::view);
|
||||
EXIT_IF(native == image.views.end());
|
||||
const auto& sampled = native->info;
|
||||
const auto& target = depth.desc.view_info;
|
||||
return (sampled.aspect & vk::ImageAspectFlagBits::eDepth) &&
|
||||
ImageRangeOverlaps(sampled.base_level, sampled.level_count,
|
||||
target.base_level, target.level_count) &&
|
||||
ImageRangeOverlaps(sampled.base_layer, sampled.layer_count,
|
||||
target.base_layer, target.layer_count);
|
||||
});
|
||||
if (feedback && !m_context.GetGraphics().attachment_feedback_loop_enabled) {
|
||||
EXIT("depth attachment feedback loop is not supported by the host\n");
|
||||
}
|
||||
const auto layout = feedback ? vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT
|
||||
: depth_attachment_layout(depth);
|
||||
// The attachment store writes even when guest depth/stencil tests do not.
|
||||
const auto access = vk::AccessFlagBits2::eDepthStencilAttachmentRead |
|
||||
vk::AccessFlagBits2::eDepthStencilAttachmentWrite;
|
||||
@@ -1089,7 +1111,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
||||
index_binding = PrepareIndexBuffer(buffer, index_source);
|
||||
}
|
||||
const auto rendering =
|
||||
AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info);
|
||||
AcquireRenderTargets(buffer, state.color_info, state.color_count, state.depth_info,
|
||||
bindings.pixel);
|
||||
|
||||
if (log_pipeline_phase) {
|
||||
LogDrawPhase(draw.name, "CreatePipeline");
|
||||
@@ -1142,6 +1165,13 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
|
||||
|
||||
SetGraphicsDynamicParams(buffer, vk_buffer, state.vs_input_info, state.color_info,
|
||||
state.color_count, state.depth_info);
|
||||
if (m_context.GetGraphics().attachment_feedback_loop_enabled) {
|
||||
vk_buffer.setAttachmentFeedbackLoopEnableEXT(
|
||||
rendering.depth_stencil_attachment.image_layout ==
|
||||
vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT
|
||||
? vk::ImageAspectFlags {vk::ImageAspectFlagBits::eDepth}
|
||||
: vk::ImageAspectFlags {});
|
||||
}
|
||||
|
||||
LogDrawPhase(draw.name, "BeginRendering");
|
||||
if (set_auto_debug) {
|
||||
|
||||
@@ -623,7 +623,22 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
supported_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
|
||||
supported_features2.pNext = mesh_extension ? static_cast<void*>(&supported_mesh)
|
||||
: static_cast<void*>(&supported_features13);
|
||||
const bool feedback_extensions =
|
||||
HasExtension(device_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME) &&
|
||||
HasExtension(device_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT feedback_layout {};
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT feedback_dynamic {};
|
||||
if (feedback_extensions) {
|
||||
feedback_dynamic.pNext = supported_features2.pNext;
|
||||
feedback_layout.pNext = &feedback_dynamic;
|
||||
supported_features2.pNext = &feedback_layout;
|
||||
}
|
||||
physical_device.getFeatures2(&supported_features2);
|
||||
graphics.attachment_feedback_loop_enabled =
|
||||
feedback_extensions && feedback_layout.attachmentFeedbackLoopLayout &&
|
||||
feedback_dynamic.attachmentFeedbackLoopDynamicState;
|
||||
LOGF("Vulkan depth feedback support: %s\n",
|
||||
graphics.attachment_feedback_loop_enabled ? "true" : "false");
|
||||
graphics.mesh_shader_enabled = mesh_extension && supported_mesh.meshShader;
|
||||
if (graphics.mesh_shader_enabled) {
|
||||
vk::PhysicalDeviceProperties2 properties {};
|
||||
@@ -729,8 +744,11 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
|
||||
mesh_features.pNext = &features13;
|
||||
mesh_features.meshShader = graphics.mesh_shader_enabled;
|
||||
create_info.sType = vk::StructureType::eDeviceCreateInfo;
|
||||
create_info.pNext =
|
||||
feedback_dynamic.pNext =
|
||||
mesh_extension ? static_cast<void*>(&mesh_features) : static_cast<void*>(&features13);
|
||||
create_info.pNext = graphics.attachment_feedback_loop_enabled
|
||||
? static_cast<void*>(&feedback_layout)
|
||||
: feedback_dynamic.pNext;
|
||||
create_info.flags = {};
|
||||
create_info.pQueueCreateInfos = &queue_create_info;
|
||||
create_info.queueCreateInfoCount = 1;
|
||||
@@ -1117,6 +1135,11 @@ void WindowContext::CreateVulkan() {
|
||||
device_extensions.push_back(extension);
|
||||
}
|
||||
}
|
||||
if (HasExtension(available_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME) &&
|
||||
HasExtension(available_extensions, VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME)) {
|
||||
device_extensions.push_back(VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME);
|
||||
device_extensions.push_back(VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanInitSubgroupSizeControl(graphic_ctx.physical_device, graphic_ctx);
|
||||
|
||||
@@ -478,6 +478,14 @@ struct RenderExecutorTestAccess {
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
static void CommitBindings(RenderExecutor &executor, CommandBuffer &buffer,
|
||||
const PipelineCache::Pipeline &pipeline,
|
||||
PreparedBindings &vertex, PreparedBindings &pixel) {
|
||||
std::array<PreparedBindings *, 2> stages{&vertex, &pixel};
|
||||
executor.CommitBindings(buffer, vk::PipelineBindPoint::eGraphics, pipeline,
|
||||
stages);
|
||||
}
|
||||
|
||||
static void DestroyDescriptorPipelines(
|
||||
RenderExecutor &executor,
|
||||
std::span<const PipelineCache::Pipeline> pipelines) {
|
||||
@@ -511,8 +519,9 @@ struct RenderExecutorTestAccess {
|
||||
CommandBuffer &buffer,
|
||||
RenderColorInfo *colors,
|
||||
uint32_t color_count,
|
||||
RenderDepthInfo &depth) {
|
||||
return executor.AcquireRenderTargets(buffer, colors, color_count, depth);
|
||||
RenderDepthInfo &depth,
|
||||
const std::optional<PreparedBindings> &pixel = std::nullopt) {
|
||||
return executor.AcquireRenderTargets(buffer, colors, color_count, depth, pixel);
|
||||
}
|
||||
|
||||
static void ResetBindings(RenderExecutor &executor) {
|
||||
@@ -1190,6 +1199,7 @@ struct GraphicsCase {
|
||||
vk::SampleCountFlagBits samples = vk::SampleCountFlagBits::e1;
|
||||
bool pixel_position_w = false;
|
||||
float vertex_clip_w = 1.0f;
|
||||
bool pixel_depth_export = false;
|
||||
};
|
||||
|
||||
struct CompiledShader {
|
||||
@@ -1534,6 +1544,7 @@ CompiledShader CompileFragmentCase(const GraphicsCase &test) {
|
||||
pixel_info.ps_ancillary = test.pixel_ancillary;
|
||||
pixel_info.ps_front_face = test.pixel_front_face;
|
||||
pixel_info.ps_pos_w = test.pixel_position_w;
|
||||
pixel_info.ps_depth_export_enable = test.pixel_depth_export;
|
||||
pixel_info.ps_system_input_base = 2;
|
||||
for (u32 i = 0; i < std::size(pixel_info.interpolator_settings); i++) {
|
||||
pixel_info.interpolator_settings[i] = i;
|
||||
@@ -8097,19 +8108,23 @@ public:
|
||||
}
|
||||
|
||||
std::vector<u32> ReadCachedTexel(const char *name, RenderContext &context,
|
||||
ImageId id, vk::Offset3D offset = {}) {
|
||||
ImageId id, vk::Offset3D offset = {},
|
||||
vk::Extent3D extent = {1, 1, 1}) {
|
||||
auto &scheduler = context.GetCommandScheduler();
|
||||
auto &image = context.GetTextureCache().GetImage(id);
|
||||
auto probe = CreateHostBuffer(name, image.info.bytes_per_block,
|
||||
const auto bytes = image.info.bytes_per_block * extent.width *
|
||||
extent.height * extent.depth;
|
||||
auto probe = CreateHostBuffer(name, bytes,
|
||||
vk::BufferUsageFlagBits::eTransferDst, {});
|
||||
scheduler.Current().EndRendering();
|
||||
image.Transit(vk::ImageLayout::eTransferSrcOptimal,
|
||||
vk::AccessFlagBits2::eTransferRead, {},
|
||||
scheduler.Current().Handle());
|
||||
vk::BufferImageCopy copy{};
|
||||
copy.imageSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1};
|
||||
copy.imageSubresource = {image.info.IsDepth() ? vk::ImageAspectFlagBits::eDepth
|
||||
: vk::ImageAspectFlagBits::eColor, 0, 0, 1};
|
||||
copy.imageOffset = offset;
|
||||
copy.imageExtent = vk::Extent3D{1, 1, 1};
|
||||
copy.imageExtent = extent;
|
||||
scheduler.Current().Handle().copyImageToBuffer(
|
||||
image.backing.image, vk::ImageLayout::eTransferSrcOptimal,
|
||||
probe.buffer, 1, ©);
|
||||
@@ -8125,7 +8140,7 @@ public:
|
||||
vk::PipelineStageFlagBits::eHost, {}, 0, nullptr, 1, &barrier, 0,
|
||||
nullptr);
|
||||
scheduler.Finish();
|
||||
auto result = ReadBuffer(name, probe, image.info.bytes_per_block / 4);
|
||||
auto result = ReadBuffer(name, probe, bytes / 4);
|
||||
DestroyBuffer(&probe);
|
||||
return result;
|
||||
}
|
||||
@@ -9859,7 +9874,7 @@ public:
|
||||
auto bounds_bindings = RenderExecutorTestAccess::PrepareGraphicsBindings(
|
||||
executor, bounds_vertex, bounds_pixel, true);
|
||||
const auto bounds_rendering = RenderExecutorTestAccess::AcquireRenderTargets(
|
||||
executor, scheduler.Current(), &no_color, 0, bounds_depth);
|
||||
executor, scheduler.Current(), &no_color, 0, bounds_depth, bounds_bindings.pixel);
|
||||
descriptor_pipelines.push_back(RenderExecutorTestAccess::CommitBindings(
|
||||
executor, scheduler.Current(), bounds_bindings.vertex, *bounds_bindings.pixel));
|
||||
const auto &vertex_depth = bounds_bindings.vertex.images[0];
|
||||
@@ -9946,7 +9961,7 @@ public:
|
||||
executor, shared_depth_vertex, shared_depth_pixel, true);
|
||||
const auto shared_rendering =
|
||||
RenderExecutorTestAccess::AcquireRenderTargets(
|
||||
executor, scheduler.Current(), &no_color, 0, shared_depth);
|
||||
executor, scheduler.Current(), &no_color, 0, shared_depth, shared_bindings.pixel);
|
||||
descriptor_pipelines.push_back(RenderExecutorTestAccess::CommitBindings(
|
||||
executor, scheduler.Current(), shared_bindings.vertex,
|
||||
*shared_bindings.pixel));
|
||||
@@ -11375,9 +11390,12 @@ public:
|
||||
m_device.destroyShaderModule(module, nullptr);
|
||||
}
|
||||
|
||||
void CheckPolygonModeRasterization() {
|
||||
constexpr const char *name = "PolygonModeRasterization";
|
||||
constexpr uint32_t extent = 32;
|
||||
void CheckRasterization(bool depth_feedback) {
|
||||
const char *name = depth_feedback ? "DepthAttachmentFeedback"
|
||||
: "PolygonModeRasterization";
|
||||
const uint32_t extent = depth_feedback ? 8 : 32;
|
||||
constexpr uintptr_t depth_address = 0x0000000204400000ull;
|
||||
constexpr uint64_t allocation_size = 0x20000;
|
||||
EnsureRuntimeContext();
|
||||
RenderContext context(m_runtime_context);
|
||||
auto &scheduler = context.GetCommandScheduler();
|
||||
@@ -11386,10 +11404,79 @@ public:
|
||||
HW::Shader shaders{};
|
||||
registers.SetRenderTargetMask(0xf);
|
||||
scheduler.Begin(registers, user_config, shaders);
|
||||
auto &resources = context.GetGpuResources();
|
||||
auto &cache = context.GetTextureCache();
|
||||
auto &executor = context.GetRenderExecutor();
|
||||
int64_t direct_offset = -1;
|
||||
RenderDepthInfo depth{};
|
||||
Require(name, "direct allocation",
|
||||
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
|
||||
0, Libs::LibKernel::Memory::KernelGetDirectMemorySize(),
|
||||
allocation_size, allocation_size, 0, &direct_offset) == 0,
|
||||
"depth allocation failed");
|
||||
void *mapped = reinterpret_cast<void *>(depth_address);
|
||||
Require(name, "direct mapping",
|
||||
Libs::LibKernel::Memory::KernelMapDirectMemory(
|
||||
&mapped, allocation_size, 0x3, 0x10, direct_offset,
|
||||
allocation_size) == 0 &&
|
||||
mapped == reinterpret_cast<void *>(depth_address),
|
||||
"depth mapping failed");
|
||||
std::memset(mapped, 0, allocation_size);
|
||||
const auto initial_depth = [](uint32_t x, uint32_t y) {
|
||||
return 0.125f + static_cast<float>(x + 2 * y) / 128.0f;
|
||||
};
|
||||
for (uint32_t y = 0; y < extent; y++) {
|
||||
for (uint32_t x = 0; x < extent; x++) {
|
||||
static_cast<float *>(mapped)[y * 64 + x] = initial_depth(x, y);
|
||||
}
|
||||
}
|
||||
resources.MapMemory(depth_address, allocation_size);
|
||||
if (depth_feedback) {
|
||||
depth.desc.type = BindingType::DepthTarget;
|
||||
depth.desc.info.data = {depth_address, 256 * extent};
|
||||
depth.desc.info.pixel_format = vk::Format::eD32Sfloat;
|
||||
depth.desc.info.guest_format = Prospero::BufferFormat::k32Float;
|
||||
depth.desc.info.extent = {extent, extent, 1};
|
||||
depth.desc.info.pitch = 64;
|
||||
depth.desc.info.bytes_per_block = 4;
|
||||
depth.desc.info.tile_mode = Prospero::TileMode::kLinear;
|
||||
depth.desc.info.mip_layout[0] = {0, 256 * extent, 64, extent};
|
||||
depth.desc.view_info.format = depth.desc.info.pixel_format;
|
||||
depth.desc.view_info.aspect = vk::ImageAspectFlagBits::eDepth;
|
||||
depth.desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
||||
depth.depth_test_enable = depth.depth_write_enable = true;
|
||||
depth.depth_compare_op = vk::CompareOp::eAlways;
|
||||
depth.image_id = cache.FindImage(depth.desc);
|
||||
}
|
||||
|
||||
GraphicsCase test;
|
||||
test.name = name;
|
||||
AppendVMovLiteral(&test.fragment_code, 0, 0x3f800000u);
|
||||
test.pixel_depth_export = depth_feedback;
|
||||
if (depth_feedback) {
|
||||
const ShaderTextureResource descriptor{{
|
||||
static_cast<uint32_t>(depth_address >> 8u),
|
||||
(static_cast<uint32_t>(Prospero::BufferFormat::k32Float) << 20u) |
|
||||
(((extent - 1u) & 3u) << 30u),
|
||||
((extent - 1u) >> 2u) | ((extent - 1u) << 14u),
|
||||
DstSel(4, 4, 4, 4) |
|
||||
(static_cast<uint32_t>(Prospero::ImageType::kColor2D) << 28u),
|
||||
0, 0, 0, 0}};
|
||||
test.has_user_data = true;
|
||||
std::copy_n(descriptor.fields, 8, test.user_data.begin());
|
||||
// Each fragment samples its own texel through interpolated screen UVs.
|
||||
for (uint32_t component = 0; component < 2; component++) {
|
||||
test.fragment_code.push_back(EncodeVintrp(0, 20 + component, 0, component, 0));
|
||||
test.fragment_code.push_back(EncodeVintrp(1, 20 + component, 0, component, 1));
|
||||
}
|
||||
test.fragment_code.push_back(EncodeMimg0(0x27, 1));
|
||||
test.fragment_code.push_back(EncodeMimg1(0, 20, 0, 2));
|
||||
AppendVMovLiteral(&test.fragment_code, 1, 0x3e000000u);
|
||||
test.fragment_code.push_back(EncodeVop2(0x03, 1, Vgpr(0), 1));
|
||||
test.fragment_code.push_back(EncodeExp0(0x08, 0x1, false));
|
||||
test.fragment_code.push_back(EncodeExp1(1, 1, 1, 1));
|
||||
} else {
|
||||
AppendVMovLiteral(&test.fragment_code, 0, 0x3f800000u);
|
||||
}
|
||||
test.fragment_code.push_back(EncodeExp0(0x00, 0xf));
|
||||
test.fragment_code.push_back(EncodeExp1(0, 0, 0, 0));
|
||||
AppendEnd(&test.fragment_code);
|
||||
@@ -11422,8 +11509,12 @@ public:
|
||||
}
|
||||
ShaderPixelInputInfo pixel{};
|
||||
pixel.stage.program = &pixel_program;
|
||||
pixel.stage.resources = std::move(fragment.resources);
|
||||
|
||||
RenderColorInfo color{};
|
||||
color.desc.type = BindingType::RenderTarget;
|
||||
color.desc.info.data = {depth_address + 0x10000, extent * extent * 16};
|
||||
color.desc.info.mip_layout[0] = {0, color.desc.info.data.size, extent, extent};
|
||||
color.desc.info.pixel_format = vk::Format::eR32G32B32A32Sfloat;
|
||||
color.desc.info.guest_format = Prospero::BufferFormat::k32_32_32_32Float;
|
||||
color.desc.info.extent = {extent, extent, 1};
|
||||
@@ -11431,17 +11522,13 @@ public:
|
||||
color.desc.info.bytes_per_block = 16;
|
||||
color.desc.view_info.format = color.desc.info.pixel_format;
|
||||
color.desc.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
auto &cache = context.GetTextureCache();
|
||||
color.image_id = TextureCacheTestAccess::InsertImage(cache, color.desc.info);
|
||||
auto &native = cache.GetImage(color.image_id);
|
||||
Image target{};
|
||||
target.image = native.backing.image;
|
||||
target.view = native.FindView(color.desc.view_info);
|
||||
target.width = target.height = extent;
|
||||
target.dwords_per_pixel = 4;
|
||||
const std::array<float, 18> vertices{
|
||||
color.image_id = cache.FindImage(color.desc);
|
||||
std::array<float, 18> vertices{
|
||||
-0.75f, -0.75f, 1, 1, 1, 1, 0.75f, -0.75f, 1, 1, 1, 1,
|
||||
0.0f, 0.75f, 1, 1, 1, 1};
|
||||
if (depth_feedback) {
|
||||
vertices = {-1, -1, 0, 0, 0, 1, 3, -1, 2, 0, 0, 1, -1, 3, 0, 2, 0, 1};
|
||||
}
|
||||
std::vector<u32> vertex_words(vertices.size());
|
||||
std::memcpy(vertex_words.data(), vertices.data(), sizeof(vertices));
|
||||
auto buffer = CreateHostBuffer(name, sizeof(vertices), vk::BufferUsageFlagBits::eVertexBuffer,
|
||||
@@ -11453,64 +11540,104 @@ public:
|
||||
mode.polymode_back_ptype = back;
|
||||
registers.SetModeControl(mode);
|
||||
return context.GetPipelineCache().CreateGraphicsPipeline(
|
||||
std::span{&color, 1u}, {}, vertex, scheduler.Current(), &pixel,
|
||||
std::span{&color, 1u}, depth, vertex, scheduler.Current(), &pixel,
|
||||
vk::PrimitiveTopology::eTriangleList, false, vertex_shader, pixel_shader);
|
||||
};
|
||||
auto &filled = pipeline(true, 2, 2);
|
||||
auto &wireframe = pipeline(true, 1, 1);
|
||||
Require(name, "pipeline cache", filled.pipeline != wireframe.pipeline &&
|
||||
pipeline(true, 2, 2).pipeline == filled.pipeline &&
|
||||
pipeline(false, 1, 0).pipeline == filled.pipeline,
|
||||
"polygon mode was lost from the cache key or dormant face modes were applied");
|
||||
const auto draw = [&](const PipelineCache::Pipeline &selected) {
|
||||
auto cmd = BeginCommands(name, "rasterization");
|
||||
AddImageBarrier(cmd, target.image, target.layout, vk::ImageLayout::eGeneral,
|
||||
vk::PipelineStageFlagBits::eAllCommands,
|
||||
vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||
vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
|
||||
vk::AccessFlagBits::eColorAttachmentWrite);
|
||||
vk::RenderingAttachmentInfo attachment{};
|
||||
attachment.imageView = target.view;
|
||||
attachment.imageLayout = vk::ImageLayout::eGeneral;
|
||||
attachment.loadOp = vk::AttachmentLoadOp::eClear;
|
||||
attachment.storeOp = vk::AttachmentStoreOp::eStore;
|
||||
vk::RenderingInfo rendering{};
|
||||
rendering.renderArea.extent = {extent, extent};
|
||||
rendering.layerCount = 1;
|
||||
rendering.colorAttachmentCount = 1;
|
||||
rendering.pColorAttachments = &attachment;
|
||||
cmd.beginRendering(rendering);
|
||||
RenderExecutorTestAccess::BindRenderTarget(executor, color.image_id);
|
||||
if (depth.image_id) {
|
||||
RenderExecutorTestAccess::BindRenderTarget(executor, depth.image_id);
|
||||
}
|
||||
auto bindings = RenderExecutorTestAccess::PrepareGraphicsBindings(
|
||||
executor, vertex.stage, pixel.stage, true);
|
||||
Require(name, "sampled attachment identity",
|
||||
!depth_feedback || bindings.pixel->images[0].image_id == depth.image_id,
|
||||
"the fragment must sample the depth attachment's native image");
|
||||
auto &command = scheduler.Current();
|
||||
auto rendering = RenderExecutorTestAccess::AcquireRenderTargets(
|
||||
executor, command, &color, 1, depth, bindings.pixel);
|
||||
const bool feedback_enabled = rendering.depth_stencil_attachment.image_layout ==
|
||||
vk::ImageLayout::eAttachmentFeedbackLoopOptimalEXT;
|
||||
Require(name, "per-draw depth feedback",
|
||||
feedback_enabled == (depth_feedback && depth.depth_write_enable),
|
||||
"feedback was not selected only for an overlapping fragment depth read/write");
|
||||
RenderExecutorTestAccess::CommitBindings(
|
||||
executor, command, selected, bindings.vertex, *bindings.pixel);
|
||||
rendering.color_attachments[0].is_clear = true;
|
||||
command.BeginRendering(rendering);
|
||||
auto cmd = command.Handle();
|
||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, selected.pipeline);
|
||||
const vk::Viewport viewport{0, 0, extent, extent, 0, 1};
|
||||
const vk::Viewport viewport{0, 0, static_cast<float>(extent),
|
||||
static_cast<float>(extent), 0, 1};
|
||||
const vk::Rect2D scissor{{0, 0}, {extent, extent}};
|
||||
cmd.setViewportWithCount(1, &viewport);
|
||||
cmd.setScissorWithCount(1, &scissor);
|
||||
cmd.setLineWidth(1);
|
||||
cmd.setDepthTestEnable(false);
|
||||
cmd.setDepthWriteEnable(false);
|
||||
cmd.setDepthTestEnable(depth.depth_test_enable);
|
||||
cmd.setDepthWriteEnable(depth.depth_write_enable);
|
||||
cmd.setDepthCompareOp(vk::CompareOp::eAlways);
|
||||
cmd.setDepthBiasEnable(false);
|
||||
const vk::Bool32 write = true;
|
||||
cmd.setColorWriteEnableEXT(1, &write);
|
||||
cmd.setAttachmentFeedbackLoopEnableEXT(
|
||||
feedback_enabled ? vk::ImageAspectFlags{vk::ImageAspectFlagBits::eDepth}
|
||||
: vk::ImageAspectFlags{});
|
||||
const vk::DeviceSize offset = 0;
|
||||
cmd.bindVertexBuffers(0, 1, &buffer.buffer, &offset);
|
||||
cmd.draw(3, 1, 0, 0);
|
||||
cmd.endRendering();
|
||||
EndSubmitAndFree(name, "rasterization", cmd);
|
||||
target.layout = vk::ImageLayout::eGeneral;
|
||||
return ReadImage(name, &target);
|
||||
command.EndRendering();
|
||||
RenderExecutorTestAccess::ResetBindings(executor);
|
||||
};
|
||||
const auto solid_pixels = draw(filled);
|
||||
const auto line_pixels = draw(wireframe);
|
||||
const auto interior = 4 * (16 * extent + 16);
|
||||
Require(name, "GPU coverage", solid_pixels[interior] == 0x3f800000u &&
|
||||
line_pixels[interior] == 0 &&
|
||||
std::ranges::any_of(line_pixels, [](u32 value) { return value == 0x3f800000u; }),
|
||||
"wireframe did not preserve triangle edges while leaving its interior empty");
|
||||
const auto read_color = [&] {
|
||||
return ReadCachedTexel(name, context, color.image_id, {}, {extent, extent, 1});
|
||||
};
|
||||
draw(filled);
|
||||
const auto solid_pixels = read_color();
|
||||
if (depth_feedback) {
|
||||
// Consecutive draws in the same submission must see each earlier depth write.
|
||||
draw(filled);
|
||||
draw(filled);
|
||||
depth.depth_write_enable = false;
|
||||
draw(filled); // Reset feedback in the same command buffer and preserve the depth writes.
|
||||
const auto repeated_pixels = read_color();
|
||||
const auto stored_depth = ReadCachedTexel(
|
||||
name, context, depth.image_id, {}, {extent, extent, 1});
|
||||
for (uint32_t y = 0; y < extent; y++) {
|
||||
for (uint32_t x = 0; x < extent; x++) {
|
||||
const auto index = y * extent + x;
|
||||
Require(name, "sampled and stored depth",
|
||||
solid_pixels[index * 4] == std::bit_cast<uint32_t>(initial_depth(x, y)) &&
|
||||
repeated_pixels[index * 4] ==
|
||||
std::bit_cast<uint32_t>(initial_depth(x, y) + 0.375f) &&
|
||||
stored_depth[index] == repeated_pixels[index * 4],
|
||||
"depth feedback lost a prior write or the read-only draw changed depth");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
auto &wireframe = pipeline(true, 1, 1);
|
||||
Require(name, "pipeline cache", filled.pipeline != wireframe.pipeline &&
|
||||
pipeline(true, 2, 2).pipeline == filled.pipeline &&
|
||||
pipeline(false, 1, 0).pipeline == filled.pipeline,
|
||||
"polygon mode was lost from the cache key or dormant face modes were applied");
|
||||
draw(wireframe);
|
||||
const auto line_pixels = read_color();
|
||||
const auto interior = 4 * (16 * extent + 16);
|
||||
Require(name, "GPU coverage", solid_pixels[interior] == 0x3f800000u &&
|
||||
line_pixels[interior] == 0 &&
|
||||
std::ranges::any_of(line_pixels, [](u32 value) { return value == 0x3f800000u; }),
|
||||
"wireframe did not preserve triangle edges while leaving its interior empty");
|
||||
}
|
||||
resources.UnmapMemory(depth_address, allocation_size);
|
||||
scheduler.Finish();
|
||||
DestroyBuffer(&buffer);
|
||||
m_device.destroyShaderModule(pixel_shader.module);
|
||||
m_device.destroyShaderModule(vertex_shader.module);
|
||||
Require(name, "release depth memory",
|
||||
Libs::LibKernel::Memory::KernelMunmap(depth_address, allocation_size) == 0 &&
|
||||
Libs::LibKernel::Memory::KernelReleaseDirectMemory(
|
||||
direct_offset, allocation_size) == 0,
|
||||
"depth mapping or allocation release failed");
|
||||
std::printf("[gpu] %-32s ok\n", name);
|
||||
}
|
||||
|
||||
@@ -12810,6 +12937,7 @@ private:
|
||||
m_runtime_context.physical_device_memory_properties = m_memory_properties;
|
||||
m_runtime_context.queue_family = m_queue_family;
|
||||
m_runtime_context.queue = m_queue;
|
||||
m_runtime_context.attachment_feedback_loop_enabled = true;
|
||||
|
||||
VmaVulkanFunctions functions{};
|
||||
functions.vkGetInstanceProcAddr =
|
||||
@@ -12918,9 +13046,13 @@ private:
|
||||
available_clip_control.pNext = &available_depth_clip;
|
||||
vk::PhysicalDeviceColorWriteEnableFeaturesEXT available_color_write{};
|
||||
available_color_write.pNext = &available_clip_control;
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT available_feedback_layout{};
|
||||
available_feedback_layout.pNext = &available_color_write;
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT available_feedback_dynamic{};
|
||||
available_feedback_dynamic.pNext = &available_feedback_layout;
|
||||
vk::PhysicalDeviceFeatures2 available_features2{};
|
||||
available_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
|
||||
available_features2.pNext = &available_color_write;
|
||||
available_features2.pNext = &available_feedback_dynamic;
|
||||
m_physical_device.getFeatures2(&available_features2);
|
||||
Require("VulkanHarness", "dispatch",
|
||||
available_features.shaderStorageImageWriteWithoutFormat == true,
|
||||
@@ -12950,7 +13082,9 @@ private:
|
||||
"vertex layer output is not supported");
|
||||
Require("VulkanHarness", "graphics", available_features.fillModeNonSolid &&
|
||||
available_depth_clip.depthClipEnable && available_clip_control.depthClipControl &&
|
||||
available_color_write.colorWriteEnable,
|
||||
available_color_write.colorWriteEnable &&
|
||||
available_feedback_layout.attachmentFeedbackLoopLayout &&
|
||||
available_feedback_dynamic.attachmentFeedbackLoopDynamicState,
|
||||
"production rasterization features are not supported");
|
||||
|
||||
float priority = 1.0f;
|
||||
@@ -12992,7 +13126,13 @@ private:
|
||||
vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write{};
|
||||
color_write.pNext = &clip_control;
|
||||
color_write.colorWriteEnable = true;
|
||||
device_info.pNext = &color_write;
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT feedback_layout{};
|
||||
feedback_layout.pNext = &color_write;
|
||||
feedback_layout.attachmentFeedbackLoopLayout = true;
|
||||
vk::PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT feedback_dynamic{};
|
||||
feedback_dynamic.pNext = &feedback_layout;
|
||||
feedback_dynamic.attachmentFeedbackLoopDynamicState = true;
|
||||
device_info.pNext = &feedback_dynamic;
|
||||
vk::PhysicalDeviceFeatures device_features{};
|
||||
device_features.shaderStorageImageWriteWithoutFormat = true;
|
||||
device_features.shaderImageGatherExtended = true;
|
||||
@@ -13006,7 +13146,9 @@ private:
|
||||
VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME,
|
||||
VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
|
||||
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME,
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME};
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME,
|
||||
VK_EXT_ATTACHMENT_FEEDBACK_LOOP_LAYOUT_EXTENSION_NAME,
|
||||
VK_EXT_ATTACHMENT_FEEDBACK_LOOP_DYNAMIC_STATE_EXTENSION_NAME};
|
||||
device_info.enabledExtensionCount = std::size(device_extensions);
|
||||
device_info.ppEnabledExtensionNames = device_extensions;
|
||||
RequireVk("VulkanHarness", "dispatch",
|
||||
@@ -28094,7 +28236,7 @@ int main(int argc, char **argv) {
|
||||
CheckDepthAttachmentWrites();
|
||||
CheckDynamicRenderingState();
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckPolygonModeRasterization();
|
||||
vulkan.CheckRasterization(false);
|
||||
vulkan.CheckRenderExecutorColor1DDiscovery();
|
||||
vulkan.CheckRenderExecutorColorVolumeDiscovery();
|
||||
vulkan.CheckRenderExecutorDccFixedClearFloat();
|
||||
@@ -28107,7 +28249,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--polygon-mode-only") == 0) {
|
||||
VulkanHarness vulkan;
|
||||
vulkan.CheckPolygonModeRasterization();
|
||||
vulkan.CheckRasterization(false);
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::strcmp(argv[1], "--htile-clear-only") == 0) {
|
||||
@@ -28159,6 +28301,7 @@ int main(int argc, char **argv) {
|
||||
CheckSampledDepthResource();
|
||||
CheckDepthTextureEncoding();
|
||||
vulkan.CheckComparisonDepthTexture();
|
||||
vulkan.CheckRasterization(true);
|
||||
RunCase(nullptr, ImageSampleA16CompareBiasRdna2AddressOrder());
|
||||
return 0;
|
||||
}
|
||||
@@ -28239,6 +28382,7 @@ int main(int argc, char **argv) {
|
||||
CheckSampledDepthResource();
|
||||
CheckDepthTextureEncoding();
|
||||
vulkan.CheckComparisonDepthTexture();
|
||||
vulkan.CheckRasterization(true);
|
||||
CheckBasicStorageTextureDescriptor();
|
||||
CheckStorageTextureLinearUploadLayout();
|
||||
CheckStorageTextureDepthTileUploadLayout();
|
||||
@@ -28307,7 +28451,7 @@ int main(int argc, char **argv) {
|
||||
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
||||
vulkan.CheckUnifiedTextureCacheFlow();
|
||||
vulkan.CheckBgra16Readback();
|
||||
vulkan.CheckPolygonModeRasterization();
|
||||
vulkan.CheckRasterization(false);
|
||||
vulkan.CheckBufferCacheDirtyGarbageCollection();
|
||||
#endif
|
||||
vulkan.CheckUnifiedImageViewCache();
|
||||
|
||||
Reference in New Issue
Block a user