blob: 160555dc6a8a190763df16f111b05fef19c06a4c [file] [log] [blame]
Austin Eng8f9523e2020-06-19 16:21:33 +00001// 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
21namespace {
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
36class 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 Eng30805552020-12-08 16:49:34 +000052
Austin Engd05777b2021-07-29 08:06:07 +000053 // TODO(crbug.com/dawn/1030): Compute pipeline compilation crashes.
54 DAWN_SUPPRESS_TEST_IF(IsLinux() && IsVulkan() && IsIntel());
55
Austin Eng8f9523e2020-06-19 16:21:33 +000056 {
Brandon Jones41c87d92021-05-21 05:01:38 +000057 utils::ComboRenderPipelineDescriptor desc;
Austin Eng8f9523e2020-06-19 16:21:33 +000058
Corentin Wallez7aec4ae2021-03-24 15:55:32 +000059 desc.vertex.module = utils::CreateShaderModule(device, R"(
Brandon Jonese87ea2b2021-04-14 17:05:07 +000060 [[stage(vertex)]]
61 fn main([[location(0)]] pos : vec2<f32>) -> [[builtin(position)]] vec4<f32> {
62 return vec4<f32>(pos, 0.0, 1.0);
Austin Eng8f9523e2020-06-19 16:21:33 +000063 })");
64
Corentin Wallez7aec4ae2021-03-24 15:55:32 +000065 desc.cFragment.module = utils::CreateShaderModule(device, R"(
Brandon Jonese87ea2b2021-04-14 17:05:07 +000066 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 Eng8f9523e2020-06-19 16:21:33 +000076 })");
77
Brandon Jonesbff9d3a2021-03-18 02:54:27 +000078 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 Eng8f9523e2020-06-19 16:21:33 +000083
Brandon Jonesbff9d3a2021-03-18 02:54:27 +000084 wgpu::DepthStencilState* depthStencil = desc.EnableDepthStencil(kDepthFormat);
85 depthStencil->depthWriteEnabled = true;
Austin Eng8f9523e2020-06-19 16:21:33 +000086
Brandon Jonesbff9d3a2021-03-18 02:54:27 +000087 desc.multisample.count = kSampleCount;
88 desc.cFragment.targetCount = 1;
89 desc.cTargets[0].format = kColorFormat;
Austin Eng8f9523e2020-06-19 16:21:33 +000090
Brandon Jonesbff9d3a2021-03-18 02:54:27 +000091 desc.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip;
Austin Eng8f9523e2020-06-19 16:21:33 +000092
Brandon Jones41c87d92021-05-21 05:01:38 +000093 drawPipeline = device.CreateRenderPipeline(&desc);
Austin Eng8f9523e2020-06-19 16:21:33 +000094 }
95 {
96 wgpu::ComputePipelineDescriptor desc = {};
Brandon Jones0d50a2c2021-06-09 18:07:32 +000097 desc.compute.entryPoint = "main";
98 desc.compute.module = utils::CreateShaderModule(device, R"(
James Price7e80cce2021-02-10 20:17:14 +000099 [[group(0), binding(0)]] var texture0 : texture_multisampled_2d<f32>;
Austin Engd05777b2021-07-29 08:06:07 +0000100 [[group(0), binding(1)]] var texture1 : texture_depth_multisampled_2d;
Austin Eng8f9523e2020-06-19 16:21:33 +0000101
Austin Engee977a02020-12-17 19:23:27 +0000102 [[block]] struct Results {
Ben Claytonc5686842021-03-17 09:48:19 +0000103 colorSamples : array<f32, 4>;
104 depthSamples : array<f32, 4>;
Austin Eng8f9523e2020-06-19 16:21:33 +0000105 };
Ben Clayton15eba9a2021-06-08 15:36:44 +0000106 [[group(0), binding(2)]] var<storage, read_write> results : Results;
Austin Eng8f9523e2020-06-19 16:21:33 +0000107
Sarah2a57db72021-06-23 19:19:06 +0000108 [[stage(compute), workgroup_size(1)]] fn main() {
Austin Engee977a02020-12-17 19:23:27 +0000109 for (var i : i32 = 0; i < 4; i = i + 1) {
110 results.colorSamples[i] = textureLoad(texture0, vec2<i32>(0, 0), i).x;
Austin Engd05777b2021-07-29 08:06:07 +0000111 results.depthSamples[i] = textureLoad(texture1, vec2<i32>(0, 0), i);
Austin Eng8f9523e2020-06-19 16:21:33 +0000112 }
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).
128TEST_P(MultisampledSamplingTest, SamplePositions) {
129 static constexpr wgpu::Extent3D kTextureSize = {1, 1, 1};
130
131 wgpu::Texture colorTexture;
132 {
133 wgpu::TextureDescriptor desc = {};
Brandon Jones27e17a62021-08-10 04:07:37 +0000134 desc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment;
Austin Eng8f9523e2020-06-19 16:21:33 +0000135 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 Jones27e17a62021-08-10 04:07:37 +0000144 desc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment;
Austin Eng8f9523e2020-06-19 16:21:33 +0000145 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 Eng8f9523e2020-06-19 16:21:33 +0000184 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 Engd05777b2021-07-29 08:06:07 +0000212 computePassEncoder.SetBindGroup(
213 0, utils::MakeBindGroup(
214 device, checkSamplePipeline.GetBindGroupLayout(0),
215 {{0, colorView},
216 {1, depthView},
217 {2, outputBuffer, alignedResultSize * sampleOffset, kResultSize}}));
Austin Eng8f9523e2020-06-19 16:21:33 +0000218 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
261DAWN_INSTANTIATE_TEST(MultisampledSamplingTest,
262 D3D12Backend(),
263 MetalBackend(),
264 OpenGLBackend(),
Stephen Whitef31b78e2020-12-04 15:59:29 +0000265 OpenGLESBackend(),
Austin Eng8f9523e2020-06-19 16:21:33 +0000266 VulkanBackend());