1 // Copyright 2019 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 "utils/ComboRenderPipelineDescriptor.h"
18 #include "utils/DawnHelpers.h"
19
20 constexpr uint32_t kRTSize = 400;
21 constexpr uint32_t kBufferElementsCount = kMinDynamicBufferOffsetAlignment / sizeof(uint32_t) + 2;
22 constexpr uint32_t kBufferSize = kBufferElementsCount * sizeof(uint32_t);
23 constexpr uint32_t kBindingSize = 8;
24
25 class DynamicBufferOffsetTests : public DawnTest {
26 protected:
SetUp()27 void SetUp() override {
28 DawnTest::SetUp();
29
30 std::array<uint32_t, kBufferElementsCount> uniformData = {0};
31
32 uniformData[0] = 1;
33 uniformData[1] = 2;
34 uniformData[uniformData.size() - 2] = 5;
35 uniformData[uniformData.size() - 1] = 6;
36
37 mUniformBuffer = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize,
38 dawn::BufferUsageBit::Uniform);
39
40 dawn::BufferDescriptor storageBufferDescriptor;
41 storageBufferDescriptor.size = kBufferSize;
42 storageBufferDescriptor.usage = dawn::BufferUsageBit::Storage |
43 dawn::BufferUsageBit::CopyDst |
44 dawn::BufferUsageBit::CopySrc;
45
46 mStorageBuffer = device.CreateBuffer(&storageBufferDescriptor);
47
48 mBindGroupLayout = utils::MakeBindGroupLayout(
49 device, {{0, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment,
50 dawn::BindingType::UniformBuffer, true},
51 {1, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment,
52 dawn::BindingType::StorageBuffer, true}});
53
54 mBindGroup = utils::MakeBindGroup(
55 device, mBindGroupLayout,
56 {{0, mUniformBuffer, 0, kBindingSize}, {1, mStorageBuffer, 0, kBindingSize}});
57 }
58 // Create objects to use as resources inside test bind groups.
59
60 dawn::BindGroup mBindGroup;
61 dawn::BindGroupLayout mBindGroupLayout;
62 dawn::Buffer mUniformBuffer;
63 dawn::Buffer mStorageBuffer;
64 dawn::Texture mColorAttachment;
65
CreateRenderPipeline()66 dawn::RenderPipeline CreateRenderPipeline() {
67 dawn::ShaderModule vsModule =
68 utils::CreateShaderModule(device, utils::ShaderStage::Vertex, R"(
69 #version 450
70 void main() {
71 const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, -1.0f), vec2(0.0f, -1.0f));
72 gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);
73 })");
74
75 dawn::ShaderModule fsModule =
76 utils::CreateShaderModule(device, utils::ShaderStage::Fragment, R"(
77 #version 450
78 layout(std140, set = 0, binding = 0) uniform uBuffer {
79 uvec2 value;
80 };
81 layout(std140, set = 0, binding = 1) buffer SBuffer {
82 uvec2 result;
83 } sBuffer;
84 layout(location = 0) out vec4 fragColor;
85 void main() {
86 sBuffer.result.xy = value.xy;
87 fragColor = vec4(value.x / 255.0f, value.y / 255.0f, 1.0f, 1.0f);
88 })");
89
90 utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
91 pipelineDescriptor.cVertexStage.module = vsModule;
92 pipelineDescriptor.cFragmentStage.module = fsModule;
93 pipelineDescriptor.cColorStates[0]->format = dawn::TextureFormat::RGBA8Unorm;
94 dawn::PipelineLayout pipelineLayout =
95 utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
96 pipelineDescriptor.layout = pipelineLayout;
97
98 return device.CreateRenderPipeline(&pipelineDescriptor);
99 }
100
CreateComputePipeline()101 dawn::ComputePipeline CreateComputePipeline() {
102 dawn::ShaderModule csModule =
103 utils::CreateShaderModule(device, utils::ShaderStage::Compute, R"(
104 #version 450
105 layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
106 layout(std140, set = 0, binding = 0) uniform UniformBuffer {
107 uvec2 value;
108 };
109 layout(std140, set = 0, binding = 1) buffer SBuffer {
110 uvec2 result;
111 } sBuffer;
112
113 void main() {
114 sBuffer.result.xy = value.xy;
115 })");
116
117 dawn::ComputePipelineDescriptor csDesc;
118 dawn::PipelineLayout pipelineLayout =
119 utils::MakeBasicPipelineLayout(device, &mBindGroupLayout);
120 csDesc.layout = pipelineLayout;
121
122 dawn::PipelineStageDescriptor computeStage;
123 computeStage.module = csModule;
124 computeStage.entryPoint = "main";
125 csDesc.computeStage = &computeStage;
126
127 return device.CreateComputePipeline(&csDesc);
128 }
129 };
130
131 // Dynamic offsets are all zero and no effect to result.
TEST_P(DynamicBufferOffsetTests,BasicRenderPipeline)132 TEST_P(DynamicBufferOffsetTests, BasicRenderPipeline) {
133 dawn::RenderPipeline pipeline = CreateRenderPipeline();
134 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
135
136 dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
137 std::array<uint64_t, 2> offsets = {0, 0};
138 dawn::RenderPassEncoder renderPassEncoder =
139 commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
140 renderPassEncoder.SetPipeline(pipeline);
141 renderPassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data());
142 renderPassEncoder.Draw(3, 1, 0, 0);
143 renderPassEncoder.EndPass();
144 dawn::CommandBuffer commands = commandEncoder.Finish();
145 queue.Submit(1, &commands);
146
147 std::vector<uint32_t> expectedData = {1, 2};
148 EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 255, 255), renderPass.color, 0, 0);
149 EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, 0, expectedData.size());
150 }
151
152 // Have non-zero dynamic offsets.
TEST_P(DynamicBufferOffsetTests,SetDynamicOffestsRenderPipeline)153 TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsRenderPipeline) {
154 dawn::RenderPipeline pipeline = CreateRenderPipeline();
155 utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
156
157 dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
158 std::array<uint64_t, 2> offsets = {kMinDynamicBufferOffsetAlignment,
159 kMinDynamicBufferOffsetAlignment};
160 dawn::RenderPassEncoder renderPassEncoder =
161 commandEncoder.BeginRenderPass(&renderPass.renderPassInfo);
162 renderPassEncoder.SetPipeline(pipeline);
163 renderPassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data());
164 renderPassEncoder.Draw(3, 1, 0, 0);
165 renderPassEncoder.EndPass();
166 dawn::CommandBuffer commands = commandEncoder.Finish();
167 queue.Submit(1, &commands);
168
169 std::vector<uint32_t> expectedData = {5, 6};
170 EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0);
171 EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer,
172 kMinDynamicBufferOffsetAlignment, expectedData.size());
173 }
174
175 // Dynamic offsets are all zero and no effect to result.
TEST_P(DynamicBufferOffsetTests,BasicComputePipeline)176 TEST_P(DynamicBufferOffsetTests, BasicComputePipeline) {
177 dawn::ComputePipeline pipeline = CreateComputePipeline();
178
179 std::array<uint64_t, 2> offsets = {0, 0};
180
181 dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
182 dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
183 computePassEncoder.SetPipeline(pipeline);
184 computePassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data());
185 computePassEncoder.Dispatch(1, 1, 1);
186 computePassEncoder.EndPass();
187 dawn::CommandBuffer commands = commandEncoder.Finish();
188 queue.Submit(1, &commands);
189
190 std::vector<uint32_t> expectedData = {1, 2};
191 EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, 0, expectedData.size());
192 }
193
194 // Have non-zero dynamic offsets.
TEST_P(DynamicBufferOffsetTests,SetDynamicOffestsComputePipeline)195 TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsComputePipeline) {
196 dawn::ComputePipeline pipeline = CreateComputePipeline();
197
198 std::array<uint64_t, 2> offsets = {kMinDynamicBufferOffsetAlignment,
199 kMinDynamicBufferOffsetAlignment};
200
201 dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder();
202 dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass();
203 computePassEncoder.SetPipeline(pipeline);
204 computePassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data());
205 computePassEncoder.Dispatch(1, 1, 1);
206 computePassEncoder.EndPass();
207 dawn::CommandBuffer commands = commandEncoder.Finish();
208 queue.Submit(1, &commands);
209
210 std::vector<uint32_t> expectedData = {5, 6};
211 EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer,
212 kMinDynamicBufferOffsetAlignment, expectedData.size());
213 }
214
215 DAWN_INSTANTIATE_TEST(DynamicBufferOffsetTests, MetalBackend, VulkanBackend);
216