Enabling sampleMask usage in RenderPipelineVk

Added the sampleMask functionality in Vulkan with some
end2end tests including tests for the shader-output
mask.

Bug: dawn:491
Change-Id: Ib39682d8857729adb4776bc2f2d8e759bf75677b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/25340
Commit-Queue: Tomek Ponitka <tommek@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Tomek Ponitka 2020-07-23 11:30:56 +00:00 committed by Commit Bot service account
parent 9061d5d154
commit 4d9cadd58a
4 changed files with 318 additions and 21 deletions

View File

@ -366,10 +366,6 @@ namespace dawn_native {
DAWN_TRY(ValidateDepthStencilStateDescriptor(device, descriptor->depthStencilState));
}
if (descriptor->sampleMask != 0xFFFFFFFF) {
return DAWN_VALIDATION_ERROR("sampleMask must be 0xFFFFFFFF (for now)");
}
if (descriptor->alphaToCoverageEnabled) {
return DAWN_VALIDATION_ERROR("alphaToCoverageEnabled isn't supported (yet)");
}
@ -571,6 +567,11 @@ namespace dawn_native {
return mAttachmentState->GetSampleCount();
}
uint32_t RenderPipelineBase::GetSampleMask() const {
ASSERT(!IsError());
return mSampleMask;
}
const AttachmentState* RenderPipelineBase::GetAttachmentState() const {
ASSERT(!IsError());

View File

@ -76,6 +76,7 @@ namespace dawn_native {
wgpu::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const;
wgpu::TextureFormat GetDepthStencilFormat() const;
uint32_t GetSampleCount() const;
uint32_t GetSampleMask() const;
const AttachmentState* GetAttachmentState() const;

View File

@ -410,7 +410,12 @@ namespace dawn_native { namespace vulkan {
multisample.rasterizationSamples = VulkanSampleCount(GetSampleCount());
multisample.sampleShadingEnable = VK_FALSE;
multisample.minSampleShading = 0.0f;
multisample.pSampleMask = nullptr;
// VkPipelineMultisampleStateCreateInfo.pSampleMask is an array of length
// ceil(rasterizationSamples / 32) and since we're passing a single uint32_t
// we have to assert that this length is indeed 1.
ASSERT(multisample.rasterizationSamples <= 32);
VkSampleMask sampleMask = GetSampleMask();
multisample.pSampleMask = &sampleMask;
multisample.alphaToCoverageEnable = VK_FALSE;
multisample.alphaToOneEnable = VK_FALSE;

View File

@ -36,7 +36,9 @@ class MultisampledRenderingTest : public DawnTest {
mDepthStencilView = mDepthStencilTexture.CreateView();
}
wgpu::RenderPipeline CreateRenderPipelineWithOneOutputForTest(bool testDepth) {
wgpu::RenderPipeline CreateRenderPipelineWithOneOutputForTest(
bool testDepth,
uint32_t sampleMask = 0xFFFFFFFF) {
const char* kFsOneOutputWithDepth =
R"(#version 450
layout(location = 0) out vec4 fragColor;
@ -61,10 +63,11 @@ class MultisampledRenderingTest : public DawnTest {
const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth;
return CreateRenderPipelineForTest(fs, 1, testDepth);
return CreateRenderPipelineForTest(fs, 1, testDepth, sampleMask);
}
wgpu::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest() {
wgpu::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest(
uint32_t sampleMask = 0xFFFFFFFF) {
const char* kFsTwoOutputs =
R"(#version 450
layout(location = 0) out vec4 fragColor1;
@ -78,7 +81,7 @@ class MultisampledRenderingTest : public DawnTest {
fragColor2 = color2;
})";
return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false);
return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false, sampleMask);
}
wgpu::Texture CreateTextureForOutputAttachment(wgpu::TextureFormat format,
@ -148,18 +151,17 @@ class MultisampledRenderingTest : public DawnTest {
void VerifyResolveTarget(const wgpu::Color& inputColor,
wgpu::Texture resolveTexture,
uint32_t mipmapLevel = 0,
uint32_t arrayLayer = 0) {
constexpr float kMSAACoverage = 0.5f;
uint32_t arrayLayer = 0,
const float MSAACoverage = 0.5f) {
// In this test we only check the pixel in the middle of the texture.
constexpr uint32_t kMiddleX = (kWidth - 1) / 2;
constexpr uint32_t kMiddleY = (kHeight - 1) / 2;
RGBA8 expectedColor;
expectedColor.r = static_cast<uint8_t>(0xFF * inputColor.r * kMSAACoverage);
expectedColor.g = static_cast<uint8_t>(0xFF * inputColor.g * kMSAACoverage);
expectedColor.b = static_cast<uint8_t>(0xFF * inputColor.b * kMSAACoverage);
expectedColor.a = static_cast<uint8_t>(0xFF * inputColor.a * kMSAACoverage);
expectedColor.r = static_cast<uint8_t>(0xFF * inputColor.r * MSAACoverage);
expectedColor.g = static_cast<uint8_t>(0xFF * inputColor.g * MSAACoverage);
expectedColor.b = static_cast<uint8_t>(0xFF * inputColor.b * MSAACoverage);
expectedColor.a = static_cast<uint8_t>(0xFF * inputColor.a * MSAACoverage);
EXPECT_TEXTURE_RGBA8_EQ(&expectedColor, resolveTexture, kMiddleX, kMiddleY, 1, 1,
mipmapLevel, arrayLayer);
@ -172,6 +174,11 @@ class MultisampledRenderingTest : public DawnTest {
constexpr static wgpu::TextureFormat kDepthStencilFormat =
wgpu::TextureFormat::Depth24PlusStencil8;
constexpr static uint32_t kFirstSampleMaskBit = 0x00000001;
constexpr static uint32_t kSecondSampleMaskBit = 0x00000002;
constexpr static uint32_t kThirdSampleMaskBit = 0x00000004;
constexpr static uint32_t kFourthSampleMaskBit = 0x00000008;
wgpu::Texture mMultisampledColorTexture;
wgpu::TextureView mMultisampledColorView;
wgpu::Texture mResolveTexture;
@ -179,10 +186,10 @@ class MultisampledRenderingTest : public DawnTest {
wgpu::Texture mDepthStencilTexture;
wgpu::TextureView mDepthStencilView;
private:
wgpu::RenderPipeline CreateRenderPipelineForTest(const char* fs,
uint32_t numColorAttachments,
bool hasDepthStencilAttachment) {
bool hasDepthStencilAttachment,
uint32_t sampleMask = 0xFFFFFFFF) {
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
// Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal,
@ -207,6 +214,7 @@ class MultisampledRenderingTest : public DawnTest {
}
pipelineDescriptor.sampleCount = kSampleCount;
pipelineDescriptor.sampleMask = sampleMask;
pipelineDescriptor.colorStateCount = numColorAttachments;
for (uint32_t i = 0; i < numColorAttachments; ++i) {
@ -368,10 +376,9 @@ TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) {
{mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1
kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2
std::array<wgpu::Color, 2> kUniformData = {kRed, kGreen};
constexpr uint32_t kSize = sizeof(kUniformData);
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize);
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kUniformData[0].r, kSize);
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
@ -515,6 +522,289 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) {
VerifyResolveTarget(kGreen, resolveTexture2, kBaseMipLevel2, kBaseArrayLayer2);
}
// Test using one multisampled color attachment with resolve target can render correctly
// with a non-default sample mask.
TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMask) {
// TODO(dawn:491): Remove this condition after enabling sampleMask usage in those
// backends.
DAWN_SKIP_TEST_IF(IsOpenGL() || IsMetal() || IsD3D12());
constexpr bool kTestDepth = false;
// The second and third samples are included,
// only the second one is covered by the triangle.
constexpr uint32_t kSampleMask = kSecondSampleMaskBit | kThirdSampleMaskBit;
constexpr float kMSAACoverage = 0.25f;
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::RenderPipeline pipeline =
CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask);
constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
constexpr uint32_t kSize = sizeof(kGreen);
// Draw a green triangle.
{
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
{mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
kTestDepth);
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize);
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage);
}
// Test using one multisampled color attachment with resolve target can render correctly
// with the final sample mask empty.
TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithEmptyFinalSampleMask) {
// TODO(dawn:491): Remove this condition after enabling sampleMask usage in those
// backends.
DAWN_SKIP_TEST_IF(IsOpenGL() || IsMetal() || IsD3D12());
constexpr bool kTestDepth = false;
// The third and fourth samples are included,
// none of which is covered by the triangle.
constexpr uint32_t kSampleMask = kThirdSampleMaskBit | kFourthSampleMaskBit;
constexpr float kMSAACoverage = 0.00f;
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::RenderPipeline pipeline =
CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask);
constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
constexpr uint32_t kSize = sizeof(kGreen);
// Draw a green triangle.
{
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
{mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
kTestDepth);
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize);
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage);
}
// Test doing MSAA resolve into multiple resolve targets works correctly with a non-default sample
// mask.
TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithSampleMask) {
// TODO(dawn:491): Remove this condition after enabling sampleMask usage in those
// backends.
DAWN_SKIP_TEST_IF(IsOpenGL() || IsMetal() || IsD3D12());
wgpu::TextureView multisampledColorView2 =
CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateView();
wgpu::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1);
wgpu::TextureView resolveView2 = resolveTexture2.CreateView();
// The first and fourth samples are included,
// only the first one is covered by the triangle.
constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kFourthSampleMaskBit;
constexpr float kMSAACoverage = 0.25f;
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(kSampleMask);
constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
constexpr bool kTestDepth = false;
// Draw a red triangle to the first color attachment, and a blue triangle to the second color
// attachment, and do MSAA resolve on two render targets in one render pass.
{
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
{mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
std::array<wgpu::Color, 2> kUniformData = {kRed, kGreen};
constexpr uint32_t kSize = sizeof(kUniformData);
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kUniformData[0].r, kSize);
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kRed, mResolveTexture, 0, 0, kMSAACoverage);
VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage);
}
// Test multisampled rendering with depth test works correctly with a non-default sample mask.
TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTestAndSampleMask) {
// TODO(dawn:491): Remove this condition after enabling sampleMask usage in those
// backends.
DAWN_SKIP_TEST_IF(IsOpenGL() || IsMetal() || IsD3D12());
// TODO(dawn:491): Find out why this test doesn't work on Windows Intel Vulkan.
DAWN_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan());
constexpr bool kTestDepth = true;
// The second sample is included in the first render pass and it's covered by the triangle.
constexpr uint32_t kSampleMaskGreen = kSecondSampleMaskBit;
// The first and second samples are included in the second render pass,
// both are covered by the triangle.
constexpr uint32_t kSampleMaskRed = kFirstSampleMaskBit | kSecondSampleMaskBit;
constexpr float kMSAACoverage = 0.50f;
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
wgpu::RenderPipeline pipelineGreen =
CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskGreen);
wgpu::RenderPipeline pipelineRed =
CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskRed);
constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
// In first render pass we draw a green triangle with depth value == 0.2f.
// We will only write to the second sample.
{
utils::ComboRenderPassDescriptor renderPass =
CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView},
wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true);
std::array<float, 5> kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color
0.2f}; // depth
constexpr uint32_t kSize = sizeof(kUniformData);
EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(),
kSize);
}
// In second render pass we draw a red triangle with depth value == 0.5f.
// We will only write to the first sample, since the second one is red with a smaller depth
// value.
{
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
{mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load,
kTestDepth);
std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color
0.5f}; // depth
constexpr uint32_t kSize = sizeof(kUniformData);
EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(),
kSize);
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
queue.Submit(1, &commandBuffer);
constexpr wgpu::Color kHalfGreenHalfRed = {(kGreen.r + kRed.r) / 2.0, (kGreen.g + kRed.g) / 2.0,
(kGreen.b + kRed.b) / 2.0,
(kGreen.a + kRed.a) / 2.0};
// The color of the pixel in the middle of mResolveTexture should be half green and half
// red if MSAA resolve runs correctly with depth test.
VerifyResolveTarget(kHalfGreenHalfRed, mResolveTexture, 0, 0, kMSAACoverage);
}
// Test using one multisampled color attachment with resolve target can render correctly
// with non-default sample mask and shader-output mask.
TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMaskAndShaderOutputMask) {
// TODO(dawn:491): Remove this condition after enabling sampleMask usage in those
// backends.
DAWN_SKIP_TEST_IF(IsOpenGL() || IsMetal() || IsD3D12());
constexpr bool kTestDepth = false;
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
// The second and third samples are included in the shader-output mask.
// The first and third samples are included in the sample mask.
// Since we're now looking at a fully covered pixel, the rasterization mask
// includes all the samples.
// Thus the final mask includes only the third sample.
constexpr float kMSAACoverage = 0.25f;
constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kThirdSampleMaskBit;
const char* fs =
R"(#version 450
layout(location = 0) out vec4 fragColor;
layout (std140, set = 0, binding = 0) uniform uBuffer {
vec4 color;
};
void main() {
fragColor = color;
gl_SampleMask[0] = 6;
})";
wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 1, false, kSampleMask);
constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
constexpr uint32_t kSize = sizeof(kGreen);
// Draw a green triangle.
{
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
{mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear,
kTestDepth);
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize);
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
queue.Submit(1, &commandBuffer);
RGBA8 expectedColor;
expectedColor.r = static_cast<uint8_t>(0xFF * kGreen.r * kMSAACoverage);
expectedColor.g = static_cast<uint8_t>(0xFF * kGreen.g * kMSAACoverage);
expectedColor.b = static_cast<uint8_t>(0xFF * kGreen.b * kMSAACoverage);
expectedColor.a = static_cast<uint8_t>(0xFF * kGreen.a * kMSAACoverage);
EXPECT_TEXTURE_RGBA8_EQ(&expectedColor, mResolveTexture, 1, 0, 1, 1, 0, 0);
}
// Test doing MSAA resolve into multiple resolve targets works correctly with a non-default
// shader-output mask.
TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithShaderOutputMask) {
// TODO(dawn:491): Remove this when SPIRV-cross adds support for SV_Coverage in HLSL.
DAWN_SKIP_TEST_IF(IsD3D12());
wgpu::TextureView multisampledColorView2 =
CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateView();
wgpu::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1);
wgpu::TextureView resolveView2 = resolveTexture2.CreateView();
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
// The second and third samples are included in the shader-output mask,
// only the first one is covered by the triangle.
constexpr float kMSAACoverage = 0.25f;
const char* fs =
R"(#version 450
layout(location = 0) out vec4 fragColor1;
layout(location = 1) out vec4 fragColor2;
layout (std140, set = 0, binding = 0) uniform uBuffer {
vec4 color1;
vec4 color2;
};
void main() {
fragColor1 = color1;
fragColor2 = color2;
gl_SampleMask[0] = 6;
})";
wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 2, false);
constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f};
constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f};
constexpr bool kTestDepth = false;
// Draw a red triangle to the first color attachment, and a blue triangle to the second color
// attachment, and do MSAA resolve on two render targets in one render pass.
{
utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest(
{mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
std::array<wgpu::Color, 2> kUniformData = {kRed, kGreen};
constexpr uint32_t kSize = sizeof(kUniformData);
EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kUniformData[0].r, kSize);
}
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
queue.Submit(1, &commandBuffer);
VerifyResolveTarget(kRed, mResolveTexture, 0, 0, kMSAACoverage);
VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage);
}
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
D3D12Backend(),
D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),