Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 1 | // Copyright 2020 The Dawn Authors |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | #include "tests/DawnTest.h" |
| 16 | |
| 17 | #include "common/Math.h" |
| 18 | #include "utils/ComboRenderPipelineDescriptor.h" |
| 19 | #include "utils/WGPUHelpers.h" |
| 20 | |
| 21 | namespace { |
| 22 | // https://github.com/gpuweb/gpuweb/issues/108 |
| 23 | // Vulkan, Metal, and D3D11 have the same standard multisample pattern. D3D12 is the same as |
| 24 | // D3D11 but it was left out of the documentation. |
| 25 | // {0.375, 0.125}, {0.875, 0.375}, {0.125 0.625}, {0.625, 0.875} |
| 26 | // In this test, we store them in -1 to 1 space because it makes it |
| 27 | // simpler to upload vertex data. Y is flipped because there is a flip between clip space and |
| 28 | // rasterization space. |
| 29 | static constexpr std::array<std::array<float, 2>, 4> kSamplePositions = { |
| 30 | {{0.375 * 2 - 1, 1 - 0.125 * 2}, |
| 31 | {0.875 * 2 - 1, 1 - 0.375 * 2}, |
| 32 | {0.125 * 2 - 1, 1 - 0.625 * 2}, |
| 33 | {0.625 * 2 - 1, 1 - 0.875 * 2}}}; |
| 34 | } // anonymous namespace |
| 35 | |
| 36 | class MultisampledSamplingTest : public DawnTest { |
| 37 | protected: |
| 38 | static constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::R8Unorm; |
| 39 | static constexpr wgpu::TextureFormat kDepthFormat = wgpu::TextureFormat::Depth32Float; |
| 40 | |
| 41 | static constexpr wgpu::TextureFormat kDepthOutFormat = wgpu::TextureFormat::R32Float; |
| 42 | static constexpr uint32_t kSampleCount = 4; |
| 43 | |
| 44 | // Render pipeline for drawing to a multisampled color and depth attachment. |
| 45 | wgpu::RenderPipeline drawPipeline; |
| 46 | |
| 47 | // A compute pipeline to texelFetch the sample locations and output the results to a buffer. |
| 48 | wgpu::ComputePipeline checkSamplePipeline; |
| 49 | |
| 50 | void SetUp() override { |
| 51 | DawnTest::SetUp(); |
Austin Eng | 3080555 | 2020-12-08 16:49:34 +0000 | [diff] [blame] | 52 | |
Austin Eng | d05777b | 2021-07-29 08:06:07 +0000 | [diff] [blame] | 53 | // TODO(crbug.com/dawn/1030): Compute pipeline compilation crashes. |
| 54 | DAWN_SUPPRESS_TEST_IF(IsLinux() && IsVulkan() && IsIntel()); |
| 55 | |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 56 | { |
Brandon Jones | 41c87d9 | 2021-05-21 05:01:38 +0000 | [diff] [blame] | 57 | utils::ComboRenderPipelineDescriptor desc; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 58 | |
Corentin Wallez | 7aec4ae | 2021-03-24 15:55:32 +0000 | [diff] [blame] | 59 | desc.vertex.module = utils::CreateShaderModule(device, R"( |
Brandon Jones | e87ea2b | 2021-04-14 17:05:07 +0000 | [diff] [blame] | 60 | [[stage(vertex)]] |
| 61 | fn main([[location(0)]] pos : vec2<f32>) -> [[builtin(position)]] vec4<f32> { |
| 62 | return vec4<f32>(pos, 0.0, 1.0); |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 63 | })"); |
| 64 | |
Corentin Wallez | 7aec4ae | 2021-03-24 15:55:32 +0000 | [diff] [blame] | 65 | desc.cFragment.module = utils::CreateShaderModule(device, R"( |
Brandon Jones | e87ea2b | 2021-04-14 17:05:07 +0000 | [diff] [blame] | 66 | struct FragmentOut { |
| 67 | [[location(0)]] color : f32; |
| 68 | [[builtin(frag_depth)]] depth : f32; |
| 69 | }; |
| 70 | |
| 71 | [[stage(fragment)]] fn main() -> FragmentOut { |
| 72 | var output : FragmentOut; |
| 73 | output.color = 1.0; |
| 74 | output.depth = 0.7; |
| 75 | return output; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 76 | })"); |
| 77 | |
Brandon Jones | bff9d3a | 2021-03-18 02:54:27 +0000 | [diff] [blame] | 78 | desc.primitive.stripIndexFormat = wgpu::IndexFormat::Uint32; |
| 79 | desc.vertex.bufferCount = 1; |
| 80 | desc.cBuffers[0].attributeCount = 1; |
| 81 | desc.cBuffers[0].arrayStride = 2 * sizeof(float); |
| 82 | desc.cAttributes[0].format = wgpu::VertexFormat::Float32x2; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 83 | |
Brandon Jones | bff9d3a | 2021-03-18 02:54:27 +0000 | [diff] [blame] | 84 | wgpu::DepthStencilState* depthStencil = desc.EnableDepthStencil(kDepthFormat); |
| 85 | depthStencil->depthWriteEnabled = true; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 86 | |
Brandon Jones | bff9d3a | 2021-03-18 02:54:27 +0000 | [diff] [blame] | 87 | desc.multisample.count = kSampleCount; |
| 88 | desc.cFragment.targetCount = 1; |
| 89 | desc.cTargets[0].format = kColorFormat; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 90 | |
Brandon Jones | bff9d3a | 2021-03-18 02:54:27 +0000 | [diff] [blame] | 91 | desc.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 92 | |
Brandon Jones | 41c87d9 | 2021-05-21 05:01:38 +0000 | [diff] [blame] | 93 | drawPipeline = device.CreateRenderPipeline(&desc); |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 94 | } |
| 95 | { |
| 96 | wgpu::ComputePipelineDescriptor desc = {}; |
Brandon Jones | 0d50a2c | 2021-06-09 18:07:32 +0000 | [diff] [blame] | 97 | desc.compute.entryPoint = "main"; |
| 98 | desc.compute.module = utils::CreateShaderModule(device, R"( |
James Price | 7e80cce | 2021-02-10 20:17:14 +0000 | [diff] [blame] | 99 | [[group(0), binding(0)]] var texture0 : texture_multisampled_2d<f32>; |
Austin Eng | d05777b | 2021-07-29 08:06:07 +0000 | [diff] [blame] | 100 | [[group(0), binding(1)]] var texture1 : texture_depth_multisampled_2d; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 101 | |
Austin Eng | ee977a0 | 2020-12-17 19:23:27 +0000 | [diff] [blame] | 102 | [[block]] struct Results { |
Ben Clayton | c568684 | 2021-03-17 09:48:19 +0000 | [diff] [blame] | 103 | colorSamples : array<f32, 4>; |
| 104 | depthSamples : array<f32, 4>; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 105 | }; |
Ben Clayton | 15eba9a | 2021-06-08 15:36:44 +0000 | [diff] [blame] | 106 | [[group(0), binding(2)]] var<storage, read_write> results : Results; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 107 | |
Sarah | 2a57db7 | 2021-06-23 19:19:06 +0000 | [diff] [blame] | 108 | [[stage(compute), workgroup_size(1)]] fn main() { |
Austin Eng | ee977a0 | 2020-12-17 19:23:27 +0000 | [diff] [blame] | 109 | for (var i : i32 = 0; i < 4; i = i + 1) { |
| 110 | results.colorSamples[i] = textureLoad(texture0, vec2<i32>(0, 0), i).x; |
Austin Eng | d05777b | 2021-07-29 08:06:07 +0000 | [diff] [blame] | 111 | results.depthSamples[i] = textureLoad(texture1, vec2<i32>(0, 0), i); |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 112 | } |
| 113 | })"); |
| 114 | |
| 115 | checkSamplePipeline = device.CreateComputePipeline(&desc); |
| 116 | } |
| 117 | } |
| 118 | }; |
| 119 | |
| 120 | // Test that the multisampling sample positions are correct. This test works by drawing a |
| 121 | // thin quad multiple times from left to right and from top to bottom on a 1x1 canvas. |
| 122 | // Each time, the quad should cover a single sample position. |
| 123 | // After drawing, a compute shader fetches all of the samples (both color and depth), |
| 124 | // and we check that only the one covered has data. |
| 125 | // We "scan" the vertical and horizontal dimensions separately to check that the triangle |
| 126 | // must cover both the X and Y coordinates of the sample position (no false positives if |
| 127 | // it covers the X position but not the Y, or vice versa). |
| 128 | TEST_P(MultisampledSamplingTest, SamplePositions) { |
| 129 | static constexpr wgpu::Extent3D kTextureSize = {1, 1, 1}; |
| 130 | |
| 131 | wgpu::Texture colorTexture; |
| 132 | { |
| 133 | wgpu::TextureDescriptor desc = {}; |
Brandon Jones | 27e17a6 | 2021-08-10 04:07:37 +0000 | [diff] [blame] | 134 | desc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 135 | desc.size = kTextureSize; |
| 136 | desc.format = kColorFormat; |
| 137 | desc.sampleCount = kSampleCount; |
| 138 | colorTexture = device.CreateTexture(&desc); |
| 139 | } |
| 140 | |
| 141 | wgpu::Texture depthTexture; |
| 142 | { |
| 143 | wgpu::TextureDescriptor desc = {}; |
Brandon Jones | 27e17a6 | 2021-08-10 04:07:37 +0000 | [diff] [blame] | 144 | desc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment; |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 145 | desc.size = kTextureSize; |
| 146 | desc.format = kDepthFormat; |
| 147 | desc.sampleCount = kSampleCount; |
| 148 | depthTexture = device.CreateTexture(&desc); |
| 149 | } |
| 150 | |
| 151 | static constexpr float kQuadWidth = 0.075; |
| 152 | std::vector<float> vBufferData; |
| 153 | |
| 154 | // Add vertices for vertical quads |
| 155 | for (uint32_t s = 0; s < kSampleCount; ++s) { |
| 156 | // clang-format off |
| 157 | vBufferData.insert(vBufferData.end(), { |
| 158 | kSamplePositions[s][0] - kQuadWidth, -1.0, |
| 159 | kSamplePositions[s][0] - kQuadWidth, 1.0, |
| 160 | kSamplePositions[s][0] + kQuadWidth, -1.0, |
| 161 | kSamplePositions[s][0] + kQuadWidth, 1.0, |
| 162 | }); |
| 163 | // clang-format on |
| 164 | } |
| 165 | |
| 166 | // Add vertices for horizontal quads |
| 167 | for (uint32_t s = 0; s < kSampleCount; ++s) { |
| 168 | // clang-format off |
| 169 | vBufferData.insert(vBufferData.end(), { |
| 170 | -1.0, kSamplePositions[s][1] - kQuadWidth, |
| 171 | -1.0, kSamplePositions[s][1] + kQuadWidth, |
| 172 | 1.0, kSamplePositions[s][1] - kQuadWidth, |
| 173 | 1.0, kSamplePositions[s][1] + kQuadWidth, |
| 174 | }); |
| 175 | // clang-format on |
| 176 | } |
| 177 | |
| 178 | wgpu::Buffer vBuffer = utils::CreateBufferFromData( |
| 179 | device, vBufferData.data(), static_cast<uint32_t>(vBufferData.size() * sizeof(float)), |
| 180 | wgpu::BufferUsage::Vertex); |
| 181 | |
| 182 | static constexpr uint32_t kQuadNumBytes = 8 * sizeof(float); |
| 183 | |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 184 | wgpu::TextureView colorView = colorTexture.CreateView(); |
| 185 | wgpu::TextureView depthView = depthTexture.CreateView(); |
| 186 | |
| 187 | static constexpr uint64_t kResultSize = 4 * sizeof(float) + 4 * sizeof(float); |
| 188 | uint64_t alignedResultSize = Align(kResultSize, 256); |
| 189 | |
| 190 | wgpu::BufferDescriptor outputBufferDesc = {}; |
| 191 | outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc; |
| 192 | outputBufferDesc.size = alignedResultSize * 8; |
| 193 | wgpu::Buffer outputBuffer = device.CreateBuffer(&outputBufferDesc); |
| 194 | |
| 195 | wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); |
| 196 | for (uint32_t iter = 0; iter < 2; ++iter) { |
| 197 | for (uint32_t sample = 0; sample < kSampleCount; ++sample) { |
| 198 | uint32_t sampleOffset = (iter * kSampleCount + sample); |
| 199 | |
| 200 | utils::ComboRenderPassDescriptor renderPass({colorView}, depthView); |
| 201 | renderPass.cDepthStencilAttachmentInfo.clearDepth = 0.f; |
| 202 | |
| 203 | wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); |
| 204 | renderPassEncoder.SetPipeline(drawPipeline); |
| 205 | renderPassEncoder.SetVertexBuffer(0, vBuffer, kQuadNumBytes * sampleOffset, |
| 206 | kQuadNumBytes); |
| 207 | renderPassEncoder.Draw(4); |
| 208 | renderPassEncoder.EndPass(); |
| 209 | |
| 210 | wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); |
| 211 | computePassEncoder.SetPipeline(checkSamplePipeline); |
Austin Eng | d05777b | 2021-07-29 08:06:07 +0000 | [diff] [blame] | 212 | computePassEncoder.SetBindGroup( |
| 213 | 0, utils::MakeBindGroup( |
| 214 | device, checkSamplePipeline.GetBindGroupLayout(0), |
| 215 | {{0, colorView}, |
| 216 | {1, depthView}, |
| 217 | {2, outputBuffer, alignedResultSize * sampleOffset, kResultSize}})); |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 218 | computePassEncoder.Dispatch(1); |
| 219 | computePassEncoder.EndPass(); |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); |
| 224 | queue.Submit(1, &commandBuffer); |
| 225 | |
| 226 | std::array<float, 8> expectedData; |
| 227 | |
| 228 | expectedData = {1, 0, 0, 0, 0.7, 0, 0, 0}; |
| 229 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 0 * alignedResultSize, 8) |
| 230 | << "vertical sample 0"; |
| 231 | |
| 232 | expectedData = {0, 1, 0, 0, 0, 0.7, 0, 0}; |
| 233 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 1 * alignedResultSize, 8) |
| 234 | << "vertical sample 1"; |
| 235 | |
| 236 | expectedData = {0, 0, 1, 0, 0, 0, 0.7, 0}; |
| 237 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 2 * alignedResultSize, 8) |
| 238 | << "vertical sample 2"; |
| 239 | |
| 240 | expectedData = {0, 0, 0, 1, 0, 0, 0, 0.7}; |
| 241 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 3 * alignedResultSize, 8) |
| 242 | << "vertical sample 3"; |
| 243 | |
| 244 | expectedData = {1, 0, 0, 0, 0.7, 0, 0, 0}; |
| 245 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 4 * alignedResultSize, 8) |
| 246 | << "horizontal sample 0"; |
| 247 | |
| 248 | expectedData = {0, 1, 0, 0, 0, 0.7, 0, 0}; |
| 249 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 5 * alignedResultSize, 8) |
| 250 | << "horizontal sample 1"; |
| 251 | |
| 252 | expectedData = {0, 0, 1, 0, 0, 0, 0.7, 0}; |
| 253 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 6 * alignedResultSize, 8) |
| 254 | << "horizontal sample 2"; |
| 255 | |
| 256 | expectedData = {0, 0, 0, 1, 0, 0, 0, 0.7}; |
| 257 | EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 7 * alignedResultSize, 8) |
| 258 | << "horizontal sample 3"; |
| 259 | } |
| 260 | |
| 261 | DAWN_INSTANTIATE_TEST(MultisampledSamplingTest, |
| 262 | D3D12Backend(), |
| 263 | MetalBackend(), |
| 264 | OpenGLBackend(), |
Stephen White | f31b78e | 2020-12-04 15:59:29 +0000 | [diff] [blame] | 265 | OpenGLESBackend(), |
Austin Eng | 8f9523e | 2020-06-19 16:21:33 +0000 | [diff] [blame] | 266 | VulkanBackend()); |