mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-27 07:52:39 +00:00
graphics: support compute stores to 2D MSAA images
This commit is contained in:
1 parent
aafe014d0c
commit
79837b423d
11 files changed
+156
-27
No files matched your search
@@ -680,6 +680,8 @@ if(BUILD_TESTING)
|
|||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --wait-until-safe-for-rendering-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --wait-until-safe-for-rendering-only)
|
||||||
add_test(NAME depth_bits
|
add_test(NAME depth_bits
|
||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --depth-bits-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --depth-bits-only)
|
||||||
|
add_test(NAME msaa_storage
|
||||||
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --msaa-storage-only)
|
||||||
add_test(NAME pm4_context_state
|
add_test(NAME pm4_context_state
|
||||||
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --context-state-only)
|
COMMAND $<TARGET_FILE:shader_recompiler_compute_tests> --context-state-only)
|
||||||
add_test(NAME gpu_tiler
|
add_test(NAME gpu_tiler
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ ImageCreateFlags(const GraphicContext& graphics, const ImageInfo& info, vk::Imag
|
|||||||
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eColorAttachment)) {
|
if (HasFormatFeature(properties, vk::FormatFeatureFlagBits::eColorAttachment)) {
|
||||||
usage |= vk::ImageUsageFlagBits::eColorAttachment;
|
usage |= vk::ImageUsageFlagBits::eColorAttachment;
|
||||||
}
|
}
|
||||||
if (info.samples == 1) {
|
if (graphics.GetPhysicalDeviceProperties().limits.storageImageSampleCounts &
|
||||||
|
static_cast<vk::SampleCountFlagBits>(info.samples)) {
|
||||||
usage |= vk::ImageUsageFlagBits::eStorage;
|
usage |= vk::ImageUsageFlagBits::eStorage;
|
||||||
}
|
}
|
||||||
return usage;
|
return usage;
|
||||||
|
|||||||
@@ -101,6 +101,7 @@ IsSupportedStorageImageResource(const ShaderRecompiler::IR::ImageResource& resou
|
|||||||
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
(resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1D ||
|
||||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray ||
|
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim1DArray ||
|
||||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D ||
|
||||||
|
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa ||
|
||||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D ||
|
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D ||
|
||||||
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) &&
|
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) &&
|
||||||
((resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None &&
|
((resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None &&
|
||||||
|
|||||||
@@ -254,6 +254,10 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
|||||||
const bool is_3d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D &&
|
const bool is_3d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D &&
|
||||||
descriptor.Type() == Prospero::ImageType::kColor3D &&
|
descriptor.Type() == Prospero::ImageType::kColor3D &&
|
||||||
descriptor.BaseArray5() == 0;
|
descriptor.BaseArray5() == 0;
|
||||||
|
const bool is_2d_msaa =
|
||||||
|
resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa &&
|
||||||
|
descriptor.Type() == Prospero::ImageType::kColor2DMsaa && descriptor.Depth() == 0 &&
|
||||||
|
descriptor.BaseArray5() == 0;
|
||||||
TileTextureBlockLayout tile_layout {};
|
TileTextureBlockLayout tile_layout {};
|
||||||
bool supported_tile = false;
|
bool supported_tile = false;
|
||||||
switch (tile) {
|
switch (tile) {
|
||||||
@@ -284,8 +288,8 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag
|
|||||||
const bool supported_swizzle =
|
const bool supported_swizzle =
|
||||||
IsValidImageSwizzle(swizzle) &&
|
IsValidImageSwizzle(swizzle) &&
|
||||||
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
(swizzle == DstSel(4, 5, 6, 7) || !resource.read || resource.atomic);
|
||||||
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d) && supported_tile &&
|
return (is_1d || is_1d_array || is_2d || is_2d_array || is_3d || is_2d_msaa) &&
|
||||||
descriptor.BaseLevel() <= descriptor.LastLevel() &&
|
supported_tile && descriptor.BaseLevel() <= descriptor.LastLevel() &&
|
||||||
descriptor.MinLod() == 0 && supported_swizzle && descriptor.BCSwizzle() == 0 &&
|
descriptor.MinLod() == 0 && supported_swizzle && descriptor.BCSwizzle() == 0 &&
|
||||||
!descriptor.MsaaDepth();
|
!descriptor.MsaaDepth();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -609,6 +609,8 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
|||||||
device_features.depthClamp = VK_TRUE;
|
device_features.depthClamp = VK_TRUE;
|
||||||
#endif
|
#endif
|
||||||
device_features.shaderStorageImageWriteWithoutFormat = VK_TRUE;
|
device_features.shaderStorageImageWriteWithoutFormat = VK_TRUE;
|
||||||
|
device_features.shaderStorageImageMultisample =
|
||||||
|
supported_features2.features.shaderStorageImageMultisample;
|
||||||
device_features.shaderImageGatherExtended = VK_TRUE;
|
device_features.shaderImageGatherExtended = VK_TRUE;
|
||||||
device_features.independentBlend = VK_TRUE;
|
device_features.independentBlend = VK_TRUE;
|
||||||
device_features.dualSrcBlend = VK_TRUE;
|
device_features.dualSrcBlend = VK_TRUE;
|
||||||
@@ -623,6 +625,11 @@ static vk::Device VulkanCreateDevice(GraphicContext& graphics,
|
|||||||
device_features.multiViewport = VK_TRUE;
|
device_features.multiViewport = VK_TRUE;
|
||||||
device_features.fillModeNonSolid = VK_TRUE;
|
device_features.fillModeNonSolid = VK_TRUE;
|
||||||
device_features.vertexPipelineStoresAndAtomics = VK_TRUE;
|
device_features.vertexPipelineStoresAndAtomics = VK_TRUE;
|
||||||
|
// Storage image sample counts beyond 1x require the feature.
|
||||||
|
if (!device_features.shaderStorageImageMultisample) {
|
||||||
|
graphics.physical_device_properties.limits.storageImageSampleCounts =
|
||||||
|
vk::SampleCountFlagBits::e1;
|
||||||
|
}
|
||||||
graphics.sample_rate_shading_enabled = true;
|
graphics.sample_rate_shading_enabled = true;
|
||||||
device_features.shaderInt64 = VK_TRUE;
|
device_features.shaderInt64 = VK_TRUE;
|
||||||
device_features.shaderFloat64 =
|
device_features.shaderFloat64 =
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ uint32_t ImageType(EmitterState& state, const IR::ImageResource& image) {
|
|||||||
EXIT("invalid image resource class");
|
EXIT("invalid image resource class");
|
||||||
}
|
}
|
||||||
const auto& info = ImageDimensionInfoFor(image.dimension);
|
const auto& info = ImageDimensionInfoFor(image.dimension);
|
||||||
|
if (sampled == 2 && info.multisampled != 0u) {
|
||||||
|
state.builder.RequireCapability(spv::CapabilityStorageImageMultisample);
|
||||||
|
}
|
||||||
const auto scalar_type =
|
const auto scalar_type =
|
||||||
image.atomic64 ? TypeU64(state) : ImageScalarType(state, image.numeric_class);
|
image.atomic64 ? TypeU64(state) : ImageScalarType(state, image.numeric_class);
|
||||||
return state.builder.Type(spv::OpTypeImage, scalar_type,
|
return state.builder.Type(spv::OpTypeImage, scalar_type,
|
||||||
@@ -213,15 +216,20 @@ uint32_t ImageDescriptorPointer(EmitterState& state, uint32_t resource, uint32_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
|
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
|
||||||
uint32_t texel, uint32_t array_index) {
|
uint32_t texel, uint32_t array_index, uint32_t sample) {
|
||||||
const auto& image = state.program.info.images.at(resource);
|
const auto& image = state.program.info.images.at(resource);
|
||||||
EXIT_IF(image.resource_class != IR::ImageResourceClass::Storage);
|
EXIT_IF(image.resource_class != IR::ImageResourceClass::Storage);
|
||||||
if (!image.atomic) {
|
if (!image.atomic) {
|
||||||
state.builder.RequireCapability(spv::CapabilityStorageImageWriteWithoutFormat);
|
state.builder.RequireCapability(spv::CapabilityStorageImageWriteWithoutFormat);
|
||||||
}
|
}
|
||||||
const auto EmitWrite = [&](uint32_t mip) {
|
const auto EmitWrite = [&](uint32_t mip) {
|
||||||
state.builder.AddFunction(spv::OpImageWrite, LoadImageDescriptor(state, resource, mip, array_index),
|
const auto descriptor = LoadImageDescriptor(state, resource, mip, array_index);
|
||||||
coord, texel);
|
if (sample == 0u) {
|
||||||
|
state.builder.AddFunction(spv::OpImageWrite, descriptor, coord, texel);
|
||||||
|
} else {
|
||||||
|
state.builder.AddFunction(spv::OpImageWrite, descriptor, coord, texel,
|
||||||
|
spv::ImageOperandsSampleMask, sample);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if (image.mip_mode != IR::ImageMipMode::Dynamic) {
|
if (image.mip_mode != IR::ImageMipMode::Dynamic) {
|
||||||
EmitWrite(0);
|
EmitWrite(0);
|
||||||
|
|||||||
@@ -941,9 +941,12 @@ void EmitImage(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
const auto mip_lod = candidate.mip_mode == IR::ImageMipMode::Dynamic
|
const auto mip_lod = candidate.mip_mode == IR::ImageMipMode::Dynamic
|
||||||
? LodU32(ctx, mem, *address, mem.image_dimension) : 0u;
|
? LodU32(ctx, mem, *address, mem.image_dimension) : 0u;
|
||||||
const auto coord = CoordU32(ctx, mem, *address, candidate.dimension);
|
const auto coord = CoordU32(ctx, mem, *address, candidate.dimension);
|
||||||
|
const auto& dimension_info = ImageDimensionInfoFor(candidate.dimension);
|
||||||
|
const auto sample = dimension_info.multisampled != 0u
|
||||||
|
? AddressU32(ctx, mem, *address, dimension_info.coordinate_components) : 0u;
|
||||||
const auto texel = StoreTexel(ctx, selected, ctx.Arg(inst, 2),
|
const auto texel = StoreTexel(ctx, selected, ctx.Arg(inst, 2),
|
||||||
candidate.numeric_class == Prospero::TextureNumericClass::Uint);
|
candidate.numeric_class == Prospero::TextureNumericClass::Uint);
|
||||||
EmitStorageImageWrite(state, resource, mip_lod, coord, texel, array_index);
|
EmitStorageImageWrite(state, resource, mip_lod, coord, texel, array_index, sample);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -339,8 +339,9 @@ uint32_t MakeSampledImage(EmitterState& state, uint32_t resource, uint32_t sampl
|
|||||||
uint32_t mip = 0, uint32_t array_index = 0,
|
uint32_t mip = 0, uint32_t array_index = 0,
|
||||||
bool sampler_dynamic = false);
|
bool sampler_dynamic = false);
|
||||||
|
|
||||||
|
// sample is 0 (no id) for single-sampled images.
|
||||||
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
|
void EmitStorageImageWrite(EmitterState& state, uint32_t resource, uint32_t mip_lod, uint32_t coord,
|
||||||
uint32_t texel, uint32_t array_index);
|
uint32_t texel, uint32_t array_index, uint32_t sample);
|
||||||
|
|
||||||
spv::ExecutionModel ExecutionModelForStage(ShaderType stage);
|
spv::ExecutionModel ExecutionModelForStage(ShaderType stage);
|
||||||
|
|
||||||
|
|||||||
@@ -343,7 +343,7 @@ struct StageOutput {
|
|||||||
inline constexpr uint32_t FirstImageBinding = 1u;
|
inline constexpr uint32_t FirstImageBinding = 1u;
|
||||||
inline constexpr uint32_t FirstComparisonImageBinding = 22u;
|
inline constexpr uint32_t FirstComparisonImageBinding = 22u;
|
||||||
inline constexpr uint32_t FirstStorageImageBinding = 29u;
|
inline constexpr uint32_t FirstStorageImageBinding = 29u;
|
||||||
inline constexpr uint32_t ImageBindingCount = 48u;
|
inline constexpr uint32_t ImageBindingCount = 50u;
|
||||||
|
|
||||||
enum class DescriptorBindingKind : uint32_t {
|
enum class DescriptorBindingKind : uint32_t {
|
||||||
Buffers = 0u,
|
Buffers = 0u,
|
||||||
@@ -357,8 +357,8 @@ enum class DescriptorBindingKind : uint32_t {
|
|||||||
Count,
|
Count,
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Samplers) == 49u);
|
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Samplers) == 51u);
|
||||||
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Count) == 56u);
|
static_assert(static_cast<uint32_t>(DescriptorBindingKind::Count) == 58u);
|
||||||
|
|
||||||
struct PushData {
|
struct PushData {
|
||||||
static constexpr uint32_t DwordCount = 32;
|
static constexpr uint32_t DwordCount = 32;
|
||||||
@@ -419,8 +419,8 @@ DescriptorBindingForImage(const ImageResource& image) {
|
|||||||
constexpr uint32_t SampledUintBinding = 8u;
|
constexpr uint32_t SampledUintBinding = 8u;
|
||||||
constexpr uint32_t SampledSintBinding = 15u;
|
constexpr uint32_t SampledSintBinding = 15u;
|
||||||
constexpr uint32_t StorageFloatBinding = FirstStorageImageBinding;
|
constexpr uint32_t StorageFloatBinding = FirstStorageImageBinding;
|
||||||
constexpr uint32_t StorageUintBinding = StorageFloatBinding + 5u;
|
constexpr uint32_t StorageUintBinding = StorageFloatBinding + 6u;
|
||||||
constexpr uint32_t AtomicUintBinding = StorageUintBinding + 5u;
|
constexpr uint32_t AtomicUintBinding = StorageUintBinding + 6u;
|
||||||
|
|
||||||
uint32_t base = 0;
|
uint32_t base = 0;
|
||||||
bool sampled = false;
|
bool sampled = false;
|
||||||
@@ -467,10 +467,10 @@ DescriptorBindingForImage(const ImageResource& image) {
|
|||||||
case Decoder::ImageDimension::Dim2D: dimension = 2u; break;
|
case Decoder::ImageDimension::Dim2D: dimension = 2u; break;
|
||||||
case Decoder::ImageDimension::Dim2DArray: dimension = 3u; break;
|
case Decoder::ImageDimension::Dim2DArray: dimension = 3u; break;
|
||||||
case Decoder::ImageDimension::Dim2DMsaa:
|
case Decoder::ImageDimension::Dim2DMsaa:
|
||||||
if (!sampled) {
|
if (image.atomic) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
dimension = 4u;
|
dimension = sampled ? 4u : 5u;
|
||||||
break;
|
break;
|
||||||
case Decoder::ImageDimension::Dim2DMsaaArray:
|
case Decoder::ImageDimension::Dim2DMsaaArray:
|
||||||
if (!sampled) {
|
if (!sampled) {
|
||||||
|
|||||||
@@ -8547,16 +8547,16 @@ void TestShaderInfoCollection() {
|
|||||||
void TestImageBindingAbi() {
|
void TestImageBindingAbi() {
|
||||||
using NumericClass = Libs::Graphics::Prospero::TextureNumericClass;
|
using NumericClass = Libs::Graphics::Prospero::TextureNumericClass;
|
||||||
|
|
||||||
Check(ImageBindingCount == 48u &&
|
Check(ImageBindingCount == 50u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::Buffers) == 0u &&
|
static_cast<uint32_t>(DescriptorBindingKind::Buffers) == 0u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::Samplers) == 49u &&
|
static_cast<uint32_t>(DescriptorBindingKind::Samplers) == 51u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::Gds) == 50u &&
|
static_cast<uint32_t>(DescriptorBindingKind::Gds) == 52u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::BdaPagetable) == 51u &&
|
static_cast<uint32_t>(DescriptorBindingKind::BdaPagetable) == 53u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::FaultBuffer) == 52u &&
|
static_cast<uint32_t>(DescriptorBindingKind::FaultBuffer) == 54u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::FlattenedSrt) == 53u &&
|
static_cast<uint32_t>(DescriptorBindingKind::FlattenedSrt) == 55u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::ShaderData) == 54u &&
|
static_cast<uint32_t>(DescriptorBindingKind::ShaderData) == 56u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::SharedMemory) == 55u &&
|
static_cast<uint32_t>(DescriptorBindingKind::SharedMemory) == 57u &&
|
||||||
static_cast<uint32_t>(DescriptorBindingKind::Count) == 56u,
|
static_cast<uint32_t>(DescriptorBindingKind::Count) == 58u,
|
||||||
"native descriptor binding anchors changed");
|
"native descriptor binding anchors changed");
|
||||||
|
|
||||||
const std::array sampled_dimensions{
|
const std::array sampled_dimensions{
|
||||||
@@ -8615,6 +8615,8 @@ void TestImageBindingAbi() {
|
|||||||
CheckBinding(ImageResourceClass::Storage, numeric_class, dimension,
|
CheckBinding(ImageResourceClass::Storage, numeric_class, dimension,
|
||||||
false);
|
false);
|
||||||
}
|
}
|
||||||
|
CheckBinding(ImageResourceClass::Storage, numeric_class,
|
||||||
|
Decoder::ImageDimension::Dim2DMsaa, false);
|
||||||
}
|
}
|
||||||
for (const auto dimension : storage_dimensions) {
|
for (const auto dimension : storage_dimensions) {
|
||||||
CheckBinding(ImageResourceClass::Storage, NumericClass::Uint, dimension,
|
CheckBinding(ImageResourceClass::Storage, NumericClass::Uint, dimension,
|
||||||
@@ -8658,11 +8660,15 @@ void TestImageBindingAbi() {
|
|||||||
image.numeric_class = NumericClass::Sint;
|
image.numeric_class = NumericClass::Sint;
|
||||||
Check(Invalid(image), "signed storage image received a descriptor binding");
|
Check(Invalid(image), "signed storage image received a descriptor binding");
|
||||||
image.numeric_class = NumericClass::Float;
|
image.numeric_class = NumericClass::Float;
|
||||||
image.dimension = Decoder::ImageDimension::Dim2DMsaa;
|
image.dimension = Decoder::ImageDimension::Dim2DMsaaArray;
|
||||||
Check(Invalid(image),
|
Check(Invalid(image),
|
||||||
"multisampled storage image received a descriptor binding");
|
"multisampled storage array received a descriptor binding");
|
||||||
image.dimension = Decoder::ImageDimension::Dim2D;
|
image.dimension = Decoder::ImageDimension::Dim2DMsaa;
|
||||||
|
image.numeric_class = NumericClass::Uint;
|
||||||
image.atomic = true;
|
image.atomic = true;
|
||||||
|
Check(Invalid(image), "multisampled atomic image received a descriptor binding");
|
||||||
|
image.numeric_class = NumericClass::Float;
|
||||||
|
image.dimension = Decoder::ImageDimension::Dim2D;
|
||||||
Check(Invalid(image), "float atomic image received a descriptor binding");
|
Check(Invalid(image), "float atomic image received a descriptor binding");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12595,6 +12595,95 @@ public:
|
|||||||
std::printf("[gpu] %-32s ok\n", name);
|
std::printf("[gpu] %-32s ok\n", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cyberpunk 2077 clears a rect of an MSAA color target from compute, one sample per store.
|
||||||
|
void CheckMsaaStorageClear() {
|
||||||
|
constexpr const char *name = "MsaaStorageClear";
|
||||||
|
constexpr uintptr_t base = 0x0000000205000000ull;
|
||||||
|
constexpr uint64_t allocation_size = 0x10000;
|
||||||
|
// Captured CS 0xad6c86aca9c0916e: s[8:11] color, s[12:13] origin, s14 width,
|
||||||
|
// s15 height << 16 | samples.
|
||||||
|
std::vector<u32> clear{0xbfa00001u, 0xd7460001u, 0x04050611u, 0xd7460000u, 0x04010610u,
|
||||||
|
0x7da21ef9u, 0x85060001u, 0x7da8000eu, 0xbf88000fu, 0x4a08000cu,
|
||||||
|
0x4a0a020du, 0x996b800fu, 0xbeea0380u, 0xbf0a6b6au, 0xbf840009u,
|
||||||
|
0x7e0c026au, 0x7e000208u, 0x7e020209u, 0x7e04020au, 0x7e06020bu,
|
||||||
|
0x816a816au, 0xf0200f30u, 0x00000004u, 0xbf82fff5u, 0xbf810000u};
|
||||||
|
ShaderMapUserData(reinterpret_cast<uint64_t>(clear.data()),
|
||||||
|
{.type = Prospero::ShaderBinaryType::kCs,
|
||||||
|
.code_size_bytes = static_cast<u32>(clear.size() * sizeof(u32))});
|
||||||
|
EnsureRuntimeContext();
|
||||||
|
int64_t direct_offset = -1;
|
||||||
|
Require(name, "direct allocation",
|
||||||
|
Libs::LibKernel::Memory::KernelAllocateDirectMemory(
|
||||||
|
0, Libs::LibKernel::Memory::KernelGetDirectMemorySize(),
|
||||||
|
allocation_size, 0x10000, 0, &direct_offset) == 0,
|
||||||
|
"MSAA allocation failed");
|
||||||
|
void *mapped = reinterpret_cast<void *>(base);
|
||||||
|
Require(name, "direct mapping",
|
||||||
|
Libs::LibKernel::Memory::KernelMapDirectMemory(
|
||||||
|
&mapped, allocation_size, 0x3, 0x10, direct_offset, 0x10000) == 0 &&
|
||||||
|
mapped == reinterpret_cast<void *>(base),
|
||||||
|
"MSAA mapping failed");
|
||||||
|
RenderContext context(m_runtime_context);
|
||||||
|
context.InitializeGpu(nullptr);
|
||||||
|
LibKernel::Memory::InstallGpuResources(&context);
|
||||||
|
context.GetGpu().SendCommandSync([&] {
|
||||||
|
CommandProcessor processor(context, 0);
|
||||||
|
processor.BufferInit();
|
||||||
|
auto &scheduler = context.GetCommandScheduler();
|
||||||
|
auto &textures = context.GetTextureCache();
|
||||||
|
auto &shaders = processor.GetShCtx();
|
||||||
|
context.MapMemory(base, allocation_size);
|
||||||
|
// 8x8 4x MSAA RGBA8: LAST_LEVEL and MAX_MIP hold log2(samples).
|
||||||
|
const ShaderTextureResource target{{static_cast<u32>(base >> 8u),
|
||||||
|
(static_cast<u32>(Prospero::BufferFormat::k8_8_8_8UNorm) << 20u) | (3u << 30u),
|
||||||
|
1u | (7u << 14u),
|
||||||
|
DstSel(4, 5, 6, 7) | (2u << 16u) |
|
||||||
|
(static_cast<u32>(Prospero::TileMode::kRenderTarget) << 20u) |
|
||||||
|
(static_cast<u32>(Prospero::ImageType::kColor2DMsaa) << 28u),
|
||||||
|
0, 0x00700020u, 0, 0}};
|
||||||
|
ShaderRecompiler::IR::DescriptorValue value{};
|
||||||
|
value.dword_count = 8;
|
||||||
|
std::copy_n(target.fields, 8, value.dwords.begin());
|
||||||
|
ShaderRecompiler::IR::ImageResource resource{};
|
||||||
|
resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Storage;
|
||||||
|
resource.numeric_class = Prospero::TextureNumericClass::Float;
|
||||||
|
resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2DMsaa;
|
||||||
|
resource.written = true;
|
||||||
|
const auto binding =
|
||||||
|
RenderExecutorTestAccess::ResolveTexture(context.GetRenderExecutor(), resource, value);
|
||||||
|
TextureCacheTestAccess::ClearImage(textures, scheduler.Current(), binding.image_id,
|
||||||
|
{vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, {});
|
||||||
|
shaders.SetCsShader({.data_addr = reinterpret_cast<uint64_t>(clear.data()),
|
||||||
|
.num_thread_x = 8, .num_thread_y = 8, .num_thread_z = 1,
|
||||||
|
.wave_size = 64, .user_sgpr = 16, .tgid_x_en = true,
|
||||||
|
.tgid_y_en = true, .tidig_comp_cnt = 1});
|
||||||
|
const std::array<u32, 8> args{std::bit_cast<u32>(1.0f), 0, 0, std::bit_cast<u32>(1.0f),
|
||||||
|
1, 1, 2, (2u << 16u) | 4u};
|
||||||
|
for (u32 i = 0; i < 8; i++) {
|
||||||
|
shaders.SetCsUserSgpr(i, target.fields[i], HW::UserSgprType::Unknown);
|
||||||
|
shaders.SetCsUserSgpr(8u + i, args[i], HW::UserSgprType::Unknown);
|
||||||
|
}
|
||||||
|
processor.DispatchDirect(1, 1, 1, 0x41u);
|
||||||
|
for (const int row : {1, 2, 3}) {
|
||||||
|
const u32 inside = row == 3 ? 0u : 0xff0000ffu;
|
||||||
|
Require(name, "every sample in the rect",
|
||||||
|
ReadCachedTexel(name, context, binding.image_id, {0, row, 0}, {4, 1, 1}) ==
|
||||||
|
std::vector<u32>{0, inside, inside, 0},
|
||||||
|
"the compute clear missed samples or wrote outside its rect");
|
||||||
|
}
|
||||||
|
context.UnmapMemory(base, allocation_size);
|
||||||
|
scheduler.Finish();
|
||||||
|
});
|
||||||
|
context.ShutdownGpu();
|
||||||
|
LibKernel::Memory::InstallGpuResources(nullptr);
|
||||||
|
Require(name, "unmap", Libs::LibKernel::Memory::KernelMunmap(base, allocation_size) == 0,
|
||||||
|
"MSAA mapping release failed");
|
||||||
|
Require(name, "release",
|
||||||
|
Libs::LibKernel::Memory::KernelReleaseDirectMemory(direct_offset, allocation_size) == 0,
|
||||||
|
"MSAA allocation release failed");
|
||||||
|
std::printf("[gpu] %-32s ok\n", name);
|
||||||
|
}
|
||||||
|
|
||||||
void CheckSampledDccClear() {
|
void CheckSampledDccClear() {
|
||||||
constexpr const char *name = "SampledDccClear";
|
constexpr const char *name = "SampledDccClear";
|
||||||
constexpr uintptr_t base = 0x0000000205000000ull;
|
constexpr uintptr_t base = 0x0000000205000000ull;
|
||||||
@@ -21003,6 +21092,7 @@ private:
|
|||||||
device_info.pNext = &conditional_rendering;
|
device_info.pNext = &conditional_rendering;
|
||||||
vk::PhysicalDeviceFeatures device_features{};
|
vk::PhysicalDeviceFeatures device_features{};
|
||||||
device_features.shaderStorageImageWriteWithoutFormat = true;
|
device_features.shaderStorageImageWriteWithoutFormat = true;
|
||||||
|
device_features.shaderStorageImageMultisample = available_features.shaderStorageImageMultisample;
|
||||||
device_features.shaderImageGatherExtended = true;
|
device_features.shaderImageGatherExtended = true;
|
||||||
device_features.sampleRateShading = true;
|
device_features.sampleRateShading = true;
|
||||||
device_features.shaderInt64 = true;
|
device_features.shaderInt64 = true;
|
||||||
@@ -49870,6 +49960,11 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, ImageSamplePacked8<false, true>());
|
RunCase(&vulkan, ImageSamplePacked8<false, true>());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (argc == 2 && std::strcmp(argv[1], "--msaa-storage-only") == 0) {
|
||||||
|
VulkanHarness vulkan;
|
||||||
|
vulkan.CheckMsaaStorageClear();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (argc == 2 && std::strcmp(argv[1], "--depth-bits-only") == 0) {
|
if (argc == 2 && std::strcmp(argv[1], "--depth-bits-only") == 0) {
|
||||||
VulkanHarness vulkan;
|
VulkanHarness vulkan;
|
||||||
vulkan.CheckDepthBitsCopy();
|
vulkan.CheckDepthBitsCopy();
|
||||||
@@ -50768,6 +50863,7 @@ int main(int argc, char **argv) {
|
|||||||
vulkan.CheckRenderExecutorColorMetadataClear();
|
vulkan.CheckRenderExecutorColorMetadataClear();
|
||||||
vulkan.CheckSampledDccClear();
|
vulkan.CheckSampledDccClear();
|
||||||
vulkan.CheckDepthBitsCopy();
|
vulkan.CheckDepthBitsCopy();
|
||||||
|
vulkan.CheckMsaaStorageClear();
|
||||||
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
|
vulkan.CheckRenderExecutorColorStandardTileDiscovery();
|
||||||
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
|
vulkan.CheckRenderExecutorColorDepthTileDiscovery();
|
||||||
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
vulkan.CheckRenderExecutorStencilBindingDiscovery();
|
||||||
|
|||||||
Reference in new issue
Block a user