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:
parent
9061d5d154
commit
4d9cadd58a
|
@ -366,10 +366,6 @@ namespace dawn_native {
|
||||||
DAWN_TRY(ValidateDepthStencilStateDescriptor(device, descriptor->depthStencilState));
|
DAWN_TRY(ValidateDepthStencilStateDescriptor(device, descriptor->depthStencilState));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor->sampleMask != 0xFFFFFFFF) {
|
|
||||||
return DAWN_VALIDATION_ERROR("sampleMask must be 0xFFFFFFFF (for now)");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor->alphaToCoverageEnabled) {
|
if (descriptor->alphaToCoverageEnabled) {
|
||||||
return DAWN_VALIDATION_ERROR("alphaToCoverageEnabled isn't supported (yet)");
|
return DAWN_VALIDATION_ERROR("alphaToCoverageEnabled isn't supported (yet)");
|
||||||
}
|
}
|
||||||
|
@ -571,6 +567,11 @@ namespace dawn_native {
|
||||||
return mAttachmentState->GetSampleCount();
|
return mAttachmentState->GetSampleCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t RenderPipelineBase::GetSampleMask() const {
|
||||||
|
ASSERT(!IsError());
|
||||||
|
return mSampleMask;
|
||||||
|
}
|
||||||
|
|
||||||
const AttachmentState* RenderPipelineBase::GetAttachmentState() const {
|
const AttachmentState* RenderPipelineBase::GetAttachmentState() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,7 @@ namespace dawn_native {
|
||||||
wgpu::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const;
|
wgpu::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const;
|
||||||
wgpu::TextureFormat GetDepthStencilFormat() const;
|
wgpu::TextureFormat GetDepthStencilFormat() const;
|
||||||
uint32_t GetSampleCount() const;
|
uint32_t GetSampleCount() const;
|
||||||
|
uint32_t GetSampleMask() const;
|
||||||
|
|
||||||
const AttachmentState* GetAttachmentState() const;
|
const AttachmentState* GetAttachmentState() const;
|
||||||
|
|
||||||
|
|
|
@ -410,7 +410,12 @@ namespace dawn_native { namespace vulkan {
|
||||||
multisample.rasterizationSamples = VulkanSampleCount(GetSampleCount());
|
multisample.rasterizationSamples = VulkanSampleCount(GetSampleCount());
|
||||||
multisample.sampleShadingEnable = VK_FALSE;
|
multisample.sampleShadingEnable = VK_FALSE;
|
||||||
multisample.minSampleShading = 0.0f;
|
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.alphaToCoverageEnable = VK_FALSE;
|
||||||
multisample.alphaToOneEnable = VK_FALSE;
|
multisample.alphaToOneEnable = VK_FALSE;
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
mDepthStencilView = mDepthStencilTexture.CreateView();
|
mDepthStencilView = mDepthStencilTexture.CreateView();
|
||||||
}
|
}
|
||||||
|
|
||||||
wgpu::RenderPipeline CreateRenderPipelineWithOneOutputForTest(bool testDepth) {
|
wgpu::RenderPipeline CreateRenderPipelineWithOneOutputForTest(
|
||||||
|
bool testDepth,
|
||||||
|
uint32_t sampleMask = 0xFFFFFFFF) {
|
||||||
const char* kFsOneOutputWithDepth =
|
const char* kFsOneOutputWithDepth =
|
||||||
R"(#version 450
|
R"(#version 450
|
||||||
layout(location = 0) out vec4 fragColor;
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
@ -61,10 +63,11 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
|
|
||||||
const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth;
|
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 =
|
const char* kFsTwoOutputs =
|
||||||
R"(#version 450
|
R"(#version 450
|
||||||
layout(location = 0) out vec4 fragColor1;
|
layout(location = 0) out vec4 fragColor1;
|
||||||
|
@ -78,7 +81,7 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
fragColor2 = color2;
|
fragColor2 = color2;
|
||||||
})";
|
})";
|
||||||
|
|
||||||
return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false);
|
return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false, sampleMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
wgpu::Texture CreateTextureForOutputAttachment(wgpu::TextureFormat format,
|
wgpu::Texture CreateTextureForOutputAttachment(wgpu::TextureFormat format,
|
||||||
|
@ -148,18 +151,17 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
void VerifyResolveTarget(const wgpu::Color& inputColor,
|
void VerifyResolveTarget(const wgpu::Color& inputColor,
|
||||||
wgpu::Texture resolveTexture,
|
wgpu::Texture resolveTexture,
|
||||||
uint32_t mipmapLevel = 0,
|
uint32_t mipmapLevel = 0,
|
||||||
uint32_t arrayLayer = 0) {
|
uint32_t arrayLayer = 0,
|
||||||
constexpr float kMSAACoverage = 0.5f;
|
const float MSAACoverage = 0.5f) {
|
||||||
|
|
||||||
// In this test we only check the pixel in the middle of the texture.
|
// In this test we only check the pixel in the middle of the texture.
|
||||||
constexpr uint32_t kMiddleX = (kWidth - 1) / 2;
|
constexpr uint32_t kMiddleX = (kWidth - 1) / 2;
|
||||||
constexpr uint32_t kMiddleY = (kHeight - 1) / 2;
|
constexpr uint32_t kMiddleY = (kHeight - 1) / 2;
|
||||||
|
|
||||||
RGBA8 expectedColor;
|
RGBA8 expectedColor;
|
||||||
expectedColor.r = static_cast<uint8_t>(0xFF * inputColor.r * kMSAACoverage);
|
expectedColor.r = static_cast<uint8_t>(0xFF * inputColor.r * MSAACoverage);
|
||||||
expectedColor.g = static_cast<uint8_t>(0xFF * inputColor.g * kMSAACoverage);
|
expectedColor.g = static_cast<uint8_t>(0xFF * inputColor.g * MSAACoverage);
|
||||||
expectedColor.b = static_cast<uint8_t>(0xFF * inputColor.b * kMSAACoverage);
|
expectedColor.b = static_cast<uint8_t>(0xFF * inputColor.b * MSAACoverage);
|
||||||
expectedColor.a = static_cast<uint8_t>(0xFF * inputColor.a * kMSAACoverage);
|
expectedColor.a = static_cast<uint8_t>(0xFF * inputColor.a * MSAACoverage);
|
||||||
|
|
||||||
EXPECT_TEXTURE_RGBA8_EQ(&expectedColor, resolveTexture, kMiddleX, kMiddleY, 1, 1,
|
EXPECT_TEXTURE_RGBA8_EQ(&expectedColor, resolveTexture, kMiddleX, kMiddleY, 1, 1,
|
||||||
mipmapLevel, arrayLayer);
|
mipmapLevel, arrayLayer);
|
||||||
|
@ -172,6 +174,11 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
constexpr static wgpu::TextureFormat kDepthStencilFormat =
|
constexpr static wgpu::TextureFormat kDepthStencilFormat =
|
||||||
wgpu::TextureFormat::Depth24PlusStencil8;
|
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::Texture mMultisampledColorTexture;
|
||||||
wgpu::TextureView mMultisampledColorView;
|
wgpu::TextureView mMultisampledColorView;
|
||||||
wgpu::Texture mResolveTexture;
|
wgpu::Texture mResolveTexture;
|
||||||
|
@ -179,10 +186,10 @@ class MultisampledRenderingTest : public DawnTest {
|
||||||
wgpu::Texture mDepthStencilTexture;
|
wgpu::Texture mDepthStencilTexture;
|
||||||
wgpu::TextureView mDepthStencilView;
|
wgpu::TextureView mDepthStencilView;
|
||||||
|
|
||||||
private:
|
|
||||||
wgpu::RenderPipeline CreateRenderPipelineForTest(const char* fs,
|
wgpu::RenderPipeline CreateRenderPipelineForTest(const char* fs,
|
||||||
uint32_t numColorAttachments,
|
uint32_t numColorAttachments,
|
||||||
bool hasDepthStencilAttachment) {
|
bool hasDepthStencilAttachment,
|
||||||
|
uint32_t sampleMask = 0xFFFFFFFF) {
|
||||||
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
|
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
|
||||||
|
|
||||||
// Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal,
|
// 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.sampleCount = kSampleCount;
|
||||||
|
pipelineDescriptor.sampleMask = sampleMask;
|
||||||
|
|
||||||
pipelineDescriptor.colorStateCount = numColorAttachments;
|
pipelineDescriptor.colorStateCount = numColorAttachments;
|
||||||
for (uint32_t i = 0; i < numColorAttachments; ++i) {
|
for (uint32_t i = 0; i < numColorAttachments; ++i) {
|
||||||
|
@ -368,10 +376,9 @@ TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) {
|
||||||
{mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
|
{mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2},
|
||||||
wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
|
wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth);
|
||||||
|
|
||||||
std::array<float, 8> kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1
|
std::array<wgpu::Color, 2> kUniformData = {kRed, kGreen};
|
||||||
kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2
|
|
||||||
constexpr uint32_t kSize = sizeof(kUniformData);
|
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();
|
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
|
||||||
|
@ -515,6 +522,289 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) {
|
||||||
VerifyResolveTarget(kGreen, resolveTexture2, kBaseMipLevel2, kBaseArrayLayer2);
|
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,
|
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
|
||||||
D3D12Backend(),
|
D3D12Backend(),
|
||||||
D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),
|
D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),
|
||||||
|
|
Loading…
Reference in New Issue