refactor(graphics)

This commit is contained in:
nmzik
2026-09-12 13:38:47 +04:00
parent f2455c875e
commit 25a44371e3
53 changed files with 708 additions and 1315 deletions
+24 -17
View File
@@ -12,6 +12,7 @@
#include <semaphore>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
@@ -196,16 +197,22 @@ void TestRangeSet() {
!ranges.Intersects(0x1100, 0x100) &&
!ranges.Intersects(0x1240, 1),
"range set intersection did not preserve half-open boundaries");
auto intersections = ranges.Intersections(0x1070, 0x1b0);
Check(intersections.size() == 2 && intersections[0].address == 0x1070 &&
intersections[0].size == 0x90 &&
intersections[1].address == 0x1200 && intersections[1].size == 0x20,
std::vector<std::pair<uint64_t, uint64_t>> intersections;
ranges.ForEachInRange(0x1070, 0x1b0, [&](uint64_t start, uint64_t end) {
intersections.emplace_back(start, end);
});
Check(intersections.size() == 2 && intersections[0].first == 0x1070 &&
intersections[0].second == 0x1100 &&
intersections[1].first == 0x1200 && intersections[1].second == 0x1220,
"range set did not merge and intersect exact byte ranges");
ranges.Subtract(0x1040, 0x1e0);
intersections = ranges.Intersections(0x1000, 0x300);
Check(intersections.size() == 2 && intersections[0].address == 0x1000 &&
intersections[0].size == 0x40 &&
intersections[1].address == 0x1220 && intersections[1].size == 0x20,
intersections.clear();
ranges.ForEachInRange(0x1000, 0x300, [&](uint64_t start, uint64_t end) {
intersections.emplace_back(start, end);
});
Check(intersections.size() == 2 && intersections[0].first == 0x1000 &&
intersections[0].second == 0x1040 &&
intersections[1].first == 0x1220 && intersections[1].second == 0x1240,
"range set subtraction did not preserve both exact tails");
}
@@ -436,7 +443,7 @@ void TestExactDirtyIntervalsSharingTrackerPage() {
"disjoint byte dirtiness duplicated the page watcher");
exact_dirty.Subtract(address + 64, 16);
if (exact_dirty.Intersections(address, page_size).empty()) {
if (!exact_dirty.Intersects(address, page_size)) {
tracker.UnmarkRegionAsGpuModified(address, page_size);
}
Check(g_protection_calls == 1 &&
@@ -445,7 +452,7 @@ void TestExactDirtyIntervalsSharingTrackerPage() {
"draining one exact interval prematurely released its shared page");
exact_dirty.Subtract(address + 192, 32);
if (exact_dirty.Intersections(address, page_size).empty()) {
if (!exact_dirty.Intersects(address, page_size)) {
tracker.UnmarkRegionAsGpuModified(address, page_size);
}
Check(g_protection_calls == 2 &&
@@ -471,17 +478,17 @@ void TestGpuDownloadProtectionMirrors() {
tracker.MarkRegionAsGpuModified(address + 16, 32);
tracker.MarkRegionAsGpuModified(address + page_size * 2 + 16, 32);
std::vector<RangeSet::Range> visited;
std::vector<std::pair<uint64_t, uint64_t>> visited;
ResetProtectionLog();
tracker.ForEachDownloadRange<false>(
address, page_size * 3,
[&](uint64_t range_address, uint64_t range_size) noexcept {
visited.push_back({range_address, range_size});
});
Check(visited.size() == 2 && visited[0].address == address &&
visited[0].size == page_size &&
visited[1].address == address + page_size * 2 &&
visited[1].size == page_size && g_protection_calls == 0 &&
Check(visited.size() == 2 && visited[0].first == address &&
visited[0].second == page_size &&
visited[1].first == address + page_size * 2 &&
visited[1].second == page_size && g_protection_calls == 0 &&
tracker.IsRegionGpuModified(address, page_size * 3),
"non-clearing download changed protection or lost sparse ranges");
@@ -491,8 +498,8 @@ void TestGpuDownloadProtectionMirrors() {
[&](uint64_t range_address, uint64_t range_size) noexcept {
visited.push_back({range_address, range_size});
});
Check(visited.size() == 1 && visited[0].address == address &&
visited[0].size == page_size && g_protection_log.size() == 1 &&
Check(visited.size() == 1 && visited[0].first == address &&
visited[0].second == page_size && g_protection_log.size() == 1 &&
g_protection_log[0].address == address &&
g_protection_log[0].size == page_size &&
g_protection_log[0].mode == Common::VirtualMemory::Mode::Read &&
+74 -175
View File
@@ -17,7 +17,6 @@
#include "graphics/host_gpu/memoryTracker.h"
#include "graphics/host_gpu/pageManager.h"
#include "graphics/host_gpu/renderer/cache/bufferCache.h"
#include "graphics/host_gpu/renderer/cache/gpuResourceManager.h"
#include "graphics/host_gpu/renderer/cache/textureCache.h"
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
@@ -2111,16 +2110,14 @@ public:
void CheckGpuMappedRangeLifecycle() {
EnsureRuntimeContext();
auto &context = Renderer();
CommandScheduler scheduler(context, m_runtime_context);
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
context.InitializeGpu(nullptr);
auto &gpu = context.GetGpu();
GpuResourceManager resources(m_runtime_context, scheduler);
resources.SetGpu(&gpu);
auto &resources = context;
constexpr uint64_t base = 0x0000000200000000ull;
constexpr uint64_t page = 0x4000;
@@ -2159,7 +2156,6 @@ public:
resources.IsMapped(new_prt, page * 6),
"old-unmap/new-map did not replace full PRT coverage");
resources.SetGpu(nullptr);
scheduler.Finish();
context.ShutdownGpu();
std::printf("[host] %-32s ok\n", "GpuMappedRangeLifecycle");
@@ -2167,12 +2163,13 @@ public:
void CheckStreamBufferRing() {
EnsureRuntimeContext();
CommandScheduler scheduler(Renderer(), m_runtime_context);
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
GpuResourceManager resources(m_runtime_context, scheduler);
auto &resources = context;
auto &cache = resources.GetBufferCache();
constexpr std::array<std::pair<MemoryUsage, uint64_t>, 4> utilities{{
{MemoryUsage::Upload, 512ull << 20},
@@ -2800,7 +2797,7 @@ public:
ordered_finished.load(),
"submit done did not drain prior PM4 work");
auto &resources = context.GetGpuResources();
auto &resources = context;
constexpr uint64_t empty_unmap_base = 0x0000000200400000ull;
constexpr uint64_t empty_unmap_size = 0x4000;
resources.MapMemory(empty_unmap_base, empty_unmap_size);
@@ -3187,15 +3184,18 @@ public:
std::binary_semaphore release_command{0};
std::atomic<bool> shutdown_complete{false};
std::atomic<bool> submission_lane_entered{false};
bool shutdown_owner_visible = false;
resources.MapMemory(empty_unmap_base, empty_unmap_size);
gpu.SendCommand([&] {
command_entered.release();
release_command.acquire();
shutdown_owner_visible = &context.GetGpu() == &gpu;
gpu.Done();
submission_lane_entered = true;
});
command_entered.acquire();
std::jthread shutdown_thread([&] {
gpu.Shutdown();
context.ShutdownGpu();
shutdown_complete = true;
});
while (!gpu.IsStopping()) {
@@ -3207,8 +3207,13 @@ public:
shutdown_thread.join();
Require(
"GpuCommandLane", "owned shutdown completion",
shutdown_complete.load() && submission_lane_entered.load(),
"GPU owner did not drain a command that entered the submission lane");
shutdown_complete.load() && submission_lane_entered.load() &&
shutdown_owner_visible,
"GPU owner did not remain available while draining queued work");
resources.UnmapMemory(empty_unmap_base, empty_unmap_size);
Require("GpuCommandLane", "unmap after shutdown",
!resources.IsMapped(empty_unmap_base, empty_unmap_size),
"renderer retained mapped coverage after the GPU command lane stopped");
context.ShutdownGpu();
std::printf("[host] %-32s ok\n", "GpuCommandLane");
}
@@ -3459,14 +3464,13 @@ public:
constexpr uint32_t ring_fault_second_value = 0x4e5f6071u;
EnsureRuntimeContext();
auto &context = Renderer();
CommandScheduler scheduler(context, m_runtime_context);
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
context.InitializeGpu(nullptr);
auto &gpu = context.GetGpu();
int64_t direct_offset = -1;
Require(name, "direct allocation",
@@ -3487,8 +3491,7 @@ public:
std::memcpy(memory + clean_offset, &clean_value, sizeof(clean_value));
{
GpuResourceManager resources(m_runtime_context, scheduler);
resources.SetGpu(&gpu);
auto &resources = context;
auto &cache = resources.GetBufferCache();
resources.MapMemory(base, allocation_size);
@@ -4284,7 +4287,6 @@ public:
cache.ReadMemory(base + reacquire_disjoint_offset,
sizeof(reacquire_value));
resources.SetGpu(nullptr);
resources.UnmapMemory(base, allocation_size);
scheduler.Finish();
}
@@ -4393,8 +4395,8 @@ public:
constexpr uint64_t allocation_size = 0x2800000;
constexpr uint64_t allocation_alignment = 0x200000;
EnsureRuntimeContext();
auto &context = Renderer();
CommandScheduler scheduler(context, m_runtime_context);
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
@@ -4419,8 +4421,7 @@ public:
std::memcpy(memory, &initial, sizeof(initial));
{
GpuResourceManager resources(m_runtime_context, scheduler);
resources.SetGpu(&gpu);
auto &resources = context;
namespace Exception = Common::HostException;
Require(name, "guest fault handler",
Exception::InstallHandler([](const Exception::ExceptionInfo &info) {
@@ -7756,7 +7757,6 @@ public:
!boundary_buffer_cache.HasGpuDirtyBytes(final_word_address, sizeof(uint32_t)),
"a final-byte CPU read did not publish the GPU-owned final word");
resources.SetGpu(nullptr);
resources.UnmapMemory(base, allocation_size);
scheduler.Finish();
LibKernel::Memory::InstallGpuResources(nullptr);
@@ -7794,13 +7794,14 @@ public:
mapped == reinterpret_cast<void *>(base),
"BGRA16 fixed mapping failed");
CommandScheduler scheduler(Renderer(), m_runtime_context);
RenderContext context(m_runtime_context);
auto &scheduler = context.GetCommandScheduler();
HW::Context registers{};
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
{
GpuResourceManager resources(m_runtime_context, scheduler);
auto &resources = context;
resources.MapMemory(base, allocation_size);
const uint32_t pitch = TileGetTexturePitch(format, 1, tile);
TileSizeAlign total{};
@@ -7973,7 +7974,7 @@ public:
registers.SetRenderTargetMask(0x0f);
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &texture_cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -8114,7 +8115,7 @@ public:
registers.SetRenderTargetMask(0x0f);
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &texture_cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -8369,7 +8370,7 @@ public:
registers.SetRenderTargetMask(0x0f);
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &texture_cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -8442,7 +8443,7 @@ public:
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -8564,7 +8565,7 @@ public:
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -8680,7 +8681,7 @@ public:
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -8898,7 +8899,7 @@ public:
registers.SetRenderTargetMask(0x0f);
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &texture_cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -9008,7 +9009,7 @@ public:
registers.SetRenderTargetMask(0x0f);
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &texture_cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -9105,7 +9106,7 @@ public:
HW::UserConfig user_config{};
HW::Shader shaders{};
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &texture_cache = resources.GetTextureCache();
auto &executor = context.GetRenderExecutor();
resources.MapMemory(base, allocation_size);
@@ -11959,7 +11960,7 @@ public:
HW::Shader shaders{};
registers.SetRenderTargetMask(0xf);
scheduler.Begin(registers, user_config, shaders);
auto &resources = context.GetGpuResources();
auto &resources = context;
auto &cache = context.GetTextureCache();
auto &executor = context.GetRenderExecutor();
int64_t direct_offset = -1;
@@ -12108,7 +12109,7 @@ public:
mode.polymode_back_ptype = back;
mode.provoking_vtx_last = provoking_last;
registers.SetModeControl(mode);
return context.GetPipelineCache().CreateGraphicsPipeline(
return context.GetPipelineCache().GetGraphicsPipeline(
std::span{&color, 1u}, depth, vertex, scheduler.Current(), &pixel,
vk::PrimitiveTopology::eTriangleList, false, vertex_shader, pixel_shader);
};
@@ -12218,6 +12219,36 @@ public:
last_pixels[i] == (solid_pixels[i] == 0 ? 0 : 0x3e800000u),
"last-vertex flat shading changed coverage or did not use vertex two");
}
const auto blend_pipeline = [&](bool enabled, bool bypass) -> PipelineCache::Pipeline & {
auto blend = registers.GetBlendControl(0);
blend.enable = enabled;
blend.color_srcblend = static_cast<uint8_t>(Prospero::BlendFactor::kZero);
blend.alpha_srcblend = static_cast<uint8_t>(Prospero::BlendFactor::kZero);
registers.SetBlendControl(0, blend);
auto target = registers.GetRenderTarget(0).info;
target.blend_bypass = bypass;
registers.SetColorInfo(0, target);
return pipeline(true, 2, 2);
};
auto &disabled_blend = blend_pipeline(false, false);
Require(name, "effective blend pipeline cache",
blend_pipeline(false, true).pipeline == disabled_blend.pipeline &&
blend_pipeline(true, true).pipeline == disabled_blend.pipeline,
"disabled and bypassed blending did not share the same host pipeline");
auto &enabled_blend = blend_pipeline(true, false);
Require(name, "enabled blend pipeline cache",
enabled_blend.pipeline != disabled_blend.pipeline &&
blend_pipeline(true, false).pipeline == enabled_blend.pipeline,
"active blending was lost from the pipeline key or missed its cached pipeline");
draw(enabled_blend);
const auto blended_pixels = read_color();
Require(name, "active blend output",
std::ranges::all_of(blended_pixels, [](u32 value) { return value == 0; }),
"zero-factor blending did not clear the fragment color");
draw(blend_pipeline(true, true));
Require(name, "bypassed blend output", read_color() == solid_pixels,
"blend bypass did not restore the unblended fragment color and coverage");
}
resources.UnmapMemory(depth_address, allocation_size);
scheduler.Finish();
@@ -25307,7 +25338,7 @@ void CheckEmbeddedFetchVertexOffset() {
program.info.vertex_offset_sgpr = result.program.info.vertex_offset_sgpr;
vertex.stage.program = &program;
vertex.stage.resources = result.resources;
return ResolveVertexOffset(index_offset, vertex);
return ResolveDrawOffsets(index_offset, vertex).first;
};
const auto ResolveInstance = [](const CompiledShader &result) {
@@ -25319,7 +25350,7 @@ void CheckEmbeddedFetchVertexOffset() {
result.program.info.instance_offset_sgpr;
vertex.stage.program = &program;
vertex.stage.resources = result.resources;
return ResolveInstanceOffset(vertex);
return ResolveDrawOffsets(0, vertex).second;
};
const auto valid =
@@ -25848,64 +25879,6 @@ void CheckSampledDepthResource() {
std::printf("[host] %-32s ok\n", "SampledDepthResource");
}
void CheckSampledVideoOutView(RenderContext &renderer) {
auto &context = renderer.GetGraphics();
CommandScheduler scheduler(renderer, context);
ShaderRecompiler::IR::ImageResource resource{};
resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Sampled;
resource.numeric_class = Prospero::TextureNumericClass::Float;
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D;
resource.read = true;
ShaderTextureResource descriptor{};
descriptor.fields[3] = static_cast<uint32_t>(Prospero::ImageType::kColor2D)
<< 28u;
ImageInfo info{};
info.pixel_format = vk::Format::eR8G8B8A8Unorm;
info.guest_format = Prospero::BufferFormat::k8_8_8_8UNorm;
info.type = Prospero::ImageType::kColor2D;
info.extent = {1, 1, 1};
info.resources = {1, 1};
info.pitch = 1;
info.bytes_per_block = 4;
info.samples = 1;
info.tile_mode = Prospero::TileMode::kLinear;
info.mip_layout[0] = {0, 4, 1, 1};
Image image(context, scheduler, info);
image.usage.video_out = true;
Require("SampledVideoOutView", "basic 2D",
IsSupportedSampledVideoOutView(resource, descriptor, image),
"basic 2D video-out view was rejected");
const auto basic_resource = resource;
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2DArray;
const bool rejects_array_resource =
!IsSupportedSampledVideoOutView(resource, descriptor, image);
resource = basic_resource;
descriptor.fields[3] =
static_cast<uint32_t>(Prospero::ImageType::kColor2DArray) << 28u;
const bool rejects_array_descriptor =
!IsSupportedSampledVideoOutView(resource, descriptor, image);
descriptor.fields[3] = static_cast<uint32_t>(Prospero::ImageType::kColor2D)
<< 28u;
descriptor.fields[4] = 1u << 16u;
const bool rejects_base_layer =
!IsSupportedSampledVideoOutView(resource, descriptor, image);
descriptor.fields[4] = 1u;
const bool rejects_layer_count =
!IsSupportedSampledVideoOutView(resource, descriptor, image);
descriptor.fields[4] = 0;
image.info.resources.layers = 2;
const bool rejects_layered_image =
!IsSupportedSampledVideoOutView(resource, descriptor, image);
Require("SampledVideoOutView", "array hard failures",
rejects_array_resource && rejects_array_descriptor &&
rejects_base_layer && rejects_layer_count &&
rejects_layered_image,
"unsupported layered video-out view was accepted");
std::printf("[host] %-32s ok\n", "SampledVideoOutView");
}
void CheckImageTransitionState(RenderContext &renderer) {
constexpr const char *name = "ImageTransitionState";
auto &context = renderer.GetGraphics();
@@ -27382,57 +27355,6 @@ void CheckStandard64RenderTargetTileRoundTrip() {
pitch == 384 && storage.size == 0x60000 && storage.align == 0x10000,
"partial Standard64KB footprint was not padded in 128x128 blocks");
ImageInfo info{};
info.data = {0x10000, storage.size};
info.pixel_format = vk::Format::eR8G8B8A8Unorm;
info.guest_format = format;
info.type = Prospero::ImageType::kColor2D;
info.extent = {width, height, 1};
info.resources = {1, 1};
info.pitch = pitch;
info.bytes_per_block = 4;
info.samples = 1;
info.tile_mode = tile;
info.mip_layout[0] = {0, storage.size, pitch, height};
Require("Standard64RenderTarget", "support boundary",
IsSupportedStandard64RenderTarget(info) && IsTiledRenderTarget(info),
"exact Standard64KB render target was not classified as tiled");
Require("Standard64RenderTarget", "display tile boundary",
IsSupportedDisplayRenderTargetTileMode(
Prospero::TileMode::kRenderTarget) &&
!IsSupportedDisplayRenderTargetTileMode(tile),
"Standard64KB render target could alias a mode-27 display image");
auto unsupported = info;
unsupported.data.address += 4;
Require("Standard64RenderTarget", "address guard",
!IsSupportedStandard64RenderTarget(unsupported),
"unaligned Standard64KB backing was accepted");
unsupported = info;
unsupported.bytes_per_block = 8;
Require("Standard64RenderTarget", "element guard",
!IsSupportedStandard64RenderTarget(unsupported),
"unimplemented Standard64KB element size was accepted");
unsupported = info;
unsupported.pitch += 128;
Require("Standard64RenderTarget", "pitch guard",
!IsSupportedStandard64RenderTarget(unsupported),
"non-minimal Standard64KB pitch was accepted");
unsupported = info;
unsupported.data.size += 0x10000;
Require("Standard64RenderTarget", "size guard",
!IsSupportedStandard64RenderTarget(unsupported),
"non-exact Standard64KB allocation was accepted");
unsupported = info;
unsupported.resources.levels = 2;
Require("Standard64RenderTarget", "mip guard",
!IsSupportedStandard64RenderTarget(unsupported),
"unimplemented Standard64KB mip chain was accepted");
unsupported = info;
unsupported.resources.layers = 2;
Require("Standard64RenderTarget", "layer guard",
!IsSupportedStandard64RenderTarget(unsupported),
"unimplemented Standard64KB array was accepted");
std::printf("[host] %-32s ok\n", "Standard64RenderTarget");
}
@@ -27590,30 +27512,6 @@ void CheckPs5DepthRegisterDecoding() {
std::printf("[host] %-32s ok\n", "Ps5DepthRegisterDecoding");
}
void CheckStencilAttachmentAccess() {
PipelineStencilStaticState state{vk::StencilOp::eKeep, vk::StencilOp::eKeep,
vk::StencilOp::eKeep,
vk::CompareOp::eAlways};
PipelineStencilDynamicState dynamic{0xff, 0xff, 0};
Require("StencilAttachmentAccess", "always keep",
!stencil_face_accesses_attachment(state, dynamic),
"ALWAYS/KEEP state was classified as stencil access");
state.compareOp = vk::CompareOp::eEqual;
Require("StencilAttachmentAccess", "compare reads",
stencil_face_accesses_attachment(state, dynamic),
"real stencil comparison was classified as no access");
state.compareOp = vk::CompareOp::eAlways;
state.passOp = vk::StencilOp::eZero;
Require("StencilAttachmentAccess", "write operation",
stencil_face_accesses_attachment(state, dynamic),
"write-capable stencil operation was classified as no access");
dynamic.writeMask = 0;
Require("StencilAttachmentAccess", "masked write",
!stencil_face_accesses_attachment(state, dynamic),
"fully masked stencil write was classified as access");
std::printf("[host] %-32s ok\n", "StencilAttachmentAccess");
}
void CheckDepthAttachmentWrites() {
RenderDepthInfo target{};
target.desc.view_info.format = vk::Format::eD32SfloatS8Uint;
@@ -29419,6 +29317,10 @@ int main(int argc, char **argv) {
vulkan.CheckBgra16Readback();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--draw-offset-only") == 0) {
CheckEmbeddedFetchVertexOffset();
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--polygon-mode-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckRasterization(false);
@@ -29498,7 +29400,6 @@ int main(int argc, char **argv) {
if (argc == 2 && std::strcmp(argv[1], "--image-view-only") == 0) {
VulkanHarness vulkan;
CheckSampledColorViews();
CheckSampledVideoOutView(vulkan.RuntimeRenderer());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--image-transition-only") == 0) {
@@ -29550,7 +29451,6 @@ int main(int argc, char **argv) {
VulkanHarness vulkan;
CheckRenderTargetFormatContract();
CheckSampledColorViews();
CheckSampledVideoOutView(vulkan.RuntimeRenderer());
CheckImageTransitionState(vulkan.RuntimeRenderer());
CheckSampledDepthResource();
CheckDepthTextureEncoding();
@@ -29566,7 +29466,6 @@ int main(int argc, char **argv) {
CheckNativeMsaaState();
CheckPs5DepthRegisterDecoding();
CheckDepthHtileStencilCompatibility();
CheckStencilAttachmentAccess();
CheckDepthAttachmentWrites();
CheckDynamicRenderingState();
CheckDepthTargetFootprints();
+17
View File
@@ -1093,6 +1093,22 @@ constexpr uint32_t EncodeSopp(uint32_t opcode, uint32_t simm = 0) {
(simm & 0xffffu);
}
void TestShaderBufferResourceSize() {
ShaderBufferResource descriptor;
descriptor.fields[2] = UINT32_MAX;
Check(descriptor.GetSize() == UINT32_MAX,
"zero-stride buffer records must count bytes");
descriptor.fields[1] = 0x3FFFu << 16u;
Check(descriptor.GetSize() == uint64_t{0x3FFF} * UINT32_MAX,
"strided buffer byte size must preserve the full 64-bit product");
descriptor.UpdateAddress48(0x123456789ABCull);
Check(descriptor.GetSize() == uint64_t{0x3FFF} * UINT32_MAX,
"buffer address must not affect the descriptor byte size");
descriptor.fields[2] = 0;
Check(descriptor.GetSize() == 0,
"zero-record buffer must be empty even with a nonzero stride");
}
void TestNativeShaderResourceDependencies() {
const auto stages = ShaderPipelineStages(vk::ShaderStageFlagBits::eVertex |
vk::ShaderStageFlagBits::eFragment |
@@ -12738,6 +12754,7 @@ int main() {
EnsureConfigInitialized();
TestResourceDescriptorClassification();
TestShaderBufferResourceSize();
TestNativeShaderResourceDependencies();
TestNormalizedImageContracts();
TestSpirvRequirementsAnalysis();