render: use guest polygon modes

This commit is contained in:
nmzik
2026-09-06 10:56:59 +04:00
parent 6ca8201982
commit 50e78d717d
6 changed files with 217 additions and 27 deletions
+184 -16
View File
@@ -11264,6 +11264,145 @@ public:
m_device.destroyShaderModule(module, nullptr);
}
void CheckPolygonModeRasterization() {
constexpr const char *name = "PolygonModeRasterization";
constexpr uint32_t extent = 32;
EnsureRuntimeContext();
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
registers.SetRenderTargetMask(0xf);
scheduler.Begin(registers, user_config, shaders);
GraphicsCase test;
test.name = name;
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);
auto fragment = CompileFragmentCase(test);
const auto vertex_spirv = TestSpv::MakePassthroughVertexSpirv(false);
const ShaderProgram vertex_shader{1, CreateShaderModule(name, vertex_spirv)};
const ShaderProgram pixel_shader{2, CreateShaderModule(name, fragment.spirv)};
ShaderRecompiler::IR::CompiledShaderInfo vertex_program{};
vertex_program.stage = ShaderType::Vertex;
vertex_program.info.vertex_fetch_components[0] = 2;
vertex_program.info.vertex_fetch_components[1] = 4;
ShaderRecompiler::IR::CompiledShaderInfo pixel_program{};
pixel_program.stage = ShaderType::Pixel;
pixel_program.info = fragment.program.info;
pixel_program.bindings = fragment.program.bindings;
ShaderVertexInputInfo vertex{};
vertex.stage.program = &vertex_program;
vertex.resources_num = 2;
vertex.buffers_num = 1;
vertex.buffers[0].stride = 6 * sizeof(float);
vertex.buffers[0].attr_num = 2;
vertex.buffers[0].attr_indices[1] = 1;
vertex.buffers[0].attr_offsets[1] = 2 * sizeof(float);
for (uint32_t i = 0; i < 2; i++) {
const auto format = i == 0 ? Prospero::BufferFormat::k32_32Float
: Prospero::BufferFormat::k32_32_32_32Float;
vertex.resources[i].fields[3] = DstSel(4, 5, 6, 7) |
(static_cast<uint32_t>(format) << 12u);
vertex.resources_dst[i].registers_num = i == 0 ? 2 : 4;
}
ShaderPixelInputInfo pixel{};
pixel.stage.program = &pixel_program;
RenderColorInfo color{};
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};
color.desc.info.pitch = extent;
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{
-0.75f, -0.75f, 1, 1, 1, 1, 0.75f, -0.75f, 1, 1, 1, 1,
0.0f, 0.75f, 1, 1, 1, 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,
vertex_words);
const auto pipeline = [&](bool enabled, uint8_t front, uint8_t back) -> PipelineCache::Pipeline & {
HW::ModeControl mode{};
mode.poly_mode = enabled;
mode.polymode_front_ptype = front;
mode.polymode_back_ptype = back;
registers.SetModeControl(mode);
return context.GetPipelineCache().CreateGraphicsPipeline(
std::span{&color, 1u}, {}, 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);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, selected.pipeline);
const vk::Viewport viewport{0, 0, extent, 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.setDepthCompareOp(vk::CompareOp::eAlways);
cmd.setDepthBiasEnable(false);
const vk::Bool32 write = true;
cmd.setColorWriteEnableEXT(1, &write);
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);
};
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");
scheduler.Finish();
DestroyBuffer(&buffer);
m_device.destroyShaderModule(pixel_shader.module);
m_device.destroyShaderModule(vertex_shader.module);
std::printf("[gpu] %-32s ok\n", name);
}
std::vector<u32> RenderFragment(const GraphicsCase &test,
const CompiledShader &fragment) {
const auto vertex_spirv = TestSpv::MakePassthroughVertexSpirv(test.layers > 1);
@@ -11297,19 +11436,8 @@ public:
CreateHostBuffer(test.name, vertices.size() * sizeof(u32),
vk::BufferUsageFlagBits::eVertexBuffer, vertices);
auto make_module = [&](const std::vector<u32> &spirv) {
vk::ShaderModuleCreateInfo module_info{};
module_info.sType = vk::StructureType::eShaderModuleCreateInfo;
module_info.codeSize = spirv.size() * sizeof(u32);
module_info.pCode = spirv.data();
vk::ShaderModule module = nullptr;
RequireVk(test.name, "graphics",
m_device.createShaderModule(&module_info, nullptr, &module),
"vkCreateShaderModule");
return module;
};
vk::ShaderModule vertex_module = make_module(vertex_spirv);
vk::ShaderModule fragment_module = make_module(fragment.spirv);
vk::ShaderModule vertex_module = CreateShaderModule(test.name, vertex_spirv);
vk::ShaderModule fragment_module = CreateShaderModule(test.name, fragment.spirv);
const auto &fragment_bind = fragment.program.bindings;
vk::PushConstantRange push_constant_range{};
@@ -12672,9 +12800,15 @@ private:
available_features13.pNext = &available_features12;
vk::PhysicalDeviceComputeShaderDerivativesFeaturesKHR available_derivatives{};
available_derivatives.pNext = &available_features13;
vk::PhysicalDeviceDepthClipEnableFeaturesEXT available_depth_clip{};
available_depth_clip.pNext = &available_derivatives;
vk::PhysicalDeviceDepthClipControlFeaturesEXT available_clip_control{};
available_clip_control.pNext = &available_depth_clip;
vk::PhysicalDeviceColorWriteEnableFeaturesEXT available_color_write{};
available_color_write.pNext = &available_clip_control;
vk::PhysicalDeviceFeatures2 available_features2{};
available_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
available_features2.pNext = &available_derivatives;
available_features2.pNext = &available_color_write;
m_physical_device.getFeatures2(&available_features2);
Require("VulkanHarness", "dispatch",
available_features.shaderStorageImageWriteWithoutFormat == true,
@@ -12702,6 +12836,10 @@ private:
"bufferDeviceAddress is not supported");
Require("VulkanHarness", "graphics", available_features12.shaderOutputLayer == true,
"vertex layer output is not supported");
Require("VulkanHarness", "graphics", available_features.fillModeNonSolid &&
available_depth_clip.depthClipEnable && available_clip_control.depthClipControl &&
available_color_write.colorWriteEnable,
"production rasterization features are not supported");
float priority = 1.0f;
vk::DeviceQueueCreateInfo queue_info{};
@@ -12733,17 +12871,30 @@ private:
vk::PhysicalDeviceComputeShaderDerivativesFeaturesKHR derivatives{};
derivatives.pNext = &device_features13;
derivatives.computeDerivativeGroupQuads = true;
device_info.pNext = &derivatives;
vk::PhysicalDeviceDepthClipEnableFeaturesEXT depth_clip{};
depth_clip.pNext = &derivatives;
depth_clip.depthClipEnable = true;
vk::PhysicalDeviceDepthClipControlFeaturesEXT clip_control{};
clip_control.pNext = &depth_clip;
clip_control.depthClipControl = true;
vk::PhysicalDeviceColorWriteEnableFeaturesEXT color_write{};
color_write.pNext = &clip_control;
color_write.colorWriteEnable = true;
device_info.pNext = &color_write;
vk::PhysicalDeviceFeatures device_features{};
device_features.shaderStorageImageWriteWithoutFormat = true;
device_features.shaderImageGatherExtended = true;
device_features.sampleRateShading = true;
device_features.shaderInt64 = true;
device_features.fillModeNonSolid = true;
device_info.pEnabledFeatures = &device_features;
constexpr const char *device_extensions[] = {
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
VK_KHR_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME,
VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME};
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};
device_info.enabledExtensionCount = std::size(device_extensions);
device_info.ppEnabledExtensionNames = device_extensions;
RequireVk("VulkanHarness", "dispatch",
@@ -12811,6 +12962,16 @@ private:
}
}
vk::ShaderModule CreateShaderModule(const char *shader_name, const std::vector<u32> &spirv) {
vk::ShaderModuleCreateInfo info{};
info.codeSize = spirv.size() * sizeof(u32);
info.pCode = spirv.data();
vk::ShaderModule module{};
RequireVk(shader_name, "graphics", m_device.createShaderModule(&info, nullptr, &module),
"vkCreateShaderModule");
return module;
}
vk::CommandBuffer BeginCommands(const char *shader_name, const char *stage) {
vk::CommandBufferAllocateInfo cmd_alloc{};
cmd_alloc.sType = vk::StructureType::eCommandBufferAllocateInfo;
@@ -27831,6 +27992,7 @@ int main(int argc, char **argv) {
CheckDepthAttachmentWrites();
CheckDynamicRenderingState();
VulkanHarness vulkan;
vulkan.CheckPolygonModeRasterization();
vulkan.CheckRenderExecutorColor1DDiscovery();
vulkan.CheckRenderExecutorColorVolumeDiscovery();
vulkan.CheckRenderExecutorDccFixedClearFloat();
@@ -27841,6 +28003,11 @@ int main(int argc, char **argv) {
vulkan.CheckBgra16Readback();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--polygon-mode-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckPolygonModeRasterization();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--htile-clear-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckUnifiedTextureCacheFlow();
@@ -28037,6 +28204,7 @@ int main(int argc, char **argv) {
vulkan.CheckRenderExecutorStencilBindingDiscovery();
vulkan.CheckUnifiedTextureCacheFlow();
vulkan.CheckBgra16Readback();
vulkan.CheckPolygonModeRasterization();
vulkan.CheckBufferCacheDirtyGarbageCollection();
#endif
vulkan.CheckUnifiedImageViewCache();