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 "common/Math.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/TestUtils.h"
20 #include "utils/WGPUHelpers.h"
21
22 #define EXPECT_LAZY_CLEAR(N, statement) \
23 do { \
24 if (UsesWire()) { \
25 statement; \
26 } else { \
27 size_t lazyClearsBefore = dawn_native::GetLazyClearCountForTesting(device.Get()); \
28 statement; \
29 size_t lazyClearsAfter = dawn_native::GetLazyClearCountForTesting(device.Get()); \
30 EXPECT_EQ(N, lazyClearsAfter - lazyClearsBefore); \
31 } \
32 } while (0)
33
34 class TextureZeroInitTest : public DawnTest {
35 protected:
SetUp()36 void SetUp() override {
37 DawnTest::SetUp();
38 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
39 }
CreateTextureDescriptor(uint32_t mipLevelCount,uint32_t arrayLayerCount,wgpu::TextureUsage usage,wgpu::TextureFormat format)40 wgpu::TextureDescriptor CreateTextureDescriptor(uint32_t mipLevelCount,
41 uint32_t arrayLayerCount,
42 wgpu::TextureUsage usage,
43 wgpu::TextureFormat format) {
44 wgpu::TextureDescriptor descriptor;
45 descriptor.dimension = wgpu::TextureDimension::e2D;
46 descriptor.size.width = kSize;
47 descriptor.size.height = kSize;
48 descriptor.size.depthOrArrayLayers = arrayLayerCount;
49 descriptor.sampleCount = 1;
50 descriptor.format = format;
51 descriptor.mipLevelCount = mipLevelCount;
52 descriptor.usage = usage;
53 return descriptor;
54 }
CreateTextureViewDescriptor(uint32_t baseMipLevel,uint32_t baseArrayLayer,wgpu::TextureFormat format=kColorFormat)55 wgpu::TextureViewDescriptor CreateTextureViewDescriptor(
56 uint32_t baseMipLevel,
57 uint32_t baseArrayLayer,
58 wgpu::TextureFormat format = kColorFormat) {
59 wgpu::TextureViewDescriptor descriptor;
60 descriptor.format = format;
61 descriptor.baseArrayLayer = baseArrayLayer;
62 descriptor.arrayLayerCount = 1;
63 descriptor.baseMipLevel = baseMipLevel;
64 descriptor.mipLevelCount = 1;
65 descriptor.dimension = wgpu::TextureViewDimension::e2D;
66 return descriptor;
67 }
CreatePipelineForTest(float depth=0.f)68 wgpu::RenderPipeline CreatePipelineForTest(float depth = 0.f) {
69 utils::ComboRenderPipelineDescriptor pipelineDescriptor;
70 pipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest(depth);
71 const char* fs = R"(
72 ;
73 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
74 return vec4<f32>(1.0, 0.0, 0.0, 1.0);
75 }
76 )";
77 pipelineDescriptor.cFragment.module = utils::CreateShaderModule(device, fs);
78 wgpu::DepthStencilState* depthStencil = pipelineDescriptor.EnableDepthStencil();
79 depthStencil->depthCompare = wgpu::CompareFunction::Equal;
80 depthStencil->stencilFront.compare = wgpu::CompareFunction::Equal;
81
82 return device.CreateRenderPipeline(&pipelineDescriptor);
83 }
CreateBasicVertexShaderForTest(float depth=0.f)84 wgpu::ShaderModule CreateBasicVertexShaderForTest(float depth = 0.f) {
85 std::string source = R"(
86 [[stage(vertex)]]
87 fn main([[builtin(vertex_index)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
88 var pos = array<vec2<f32>, 6>(
89 vec2<f32>(-1.0, -1.0),
90 vec2<f32>(-1.0, 1.0),
91 vec2<f32>( 1.0, -1.0),
92 vec2<f32>( 1.0, 1.0),
93 vec2<f32>(-1.0, 1.0),
94 vec2<f32>( 1.0, -1.0)
95 );
96 return vec4<f32>(pos[VertexIndex], )" +
97 std::to_string(depth) + R"(, 1.0);
98 })";
99 return utils::CreateShaderModule(device, source.c_str());
100 }
CreateSampledTextureFragmentShaderForTest()101 wgpu::ShaderModule CreateSampledTextureFragmentShaderForTest() {
102 return utils::CreateShaderModule(device, R"(
103 [[group(0), binding(0)]] var texture0 : texture_2d<f32>;
104 struct FragmentOut {
105 [[location(0)]] color : vec4<f32>;
106 };
107 [[stage(fragment)]]
108 fn main([[builtin(position)]] FragCoord : vec4<f32>) -> FragmentOut {
109 var output : FragmentOut;
110 output.color = textureLoad(texture0, vec2<i32>(FragCoord.xy), 0);
111 return output;
112 }
113 )");
114 }
115
116 constexpr static uint32_t kSize = 128;
117 constexpr static uint32_t kUnalignedSize = 127;
118 // All texture formats used (RGBA8Unorm, Depth24PlusStencil8, and RGBA8Snorm, BC formats)
119 // have the same block byte size of 4.
120 constexpr static uint32_t kFormatBlockByteSize = 4;
121 constexpr static wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
122 constexpr static wgpu::TextureFormat kDepthStencilFormat =
123 wgpu::TextureFormat::Depth24PlusStencil8;
124 constexpr static wgpu::TextureFormat kNonrenderableColorFormat =
125 wgpu::TextureFormat::RGBA8Snorm;
126 };
127
128 // This tests that the code path of CopyTextureToBuffer clears correctly to Zero after first usage
TEST_P(TextureZeroInitTest,CopyTextureToBufferSource)129 TEST_P(TextureZeroInitTest, CopyTextureToBufferSource) {
130 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
131 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kColorFormat);
132 wgpu::Texture texture = device.CreateTexture(&descriptor);
133
134 // Texture's first usage is in EXPECT_PIXEL_RGBA8_EQ's call to CopyTextureToBuffer
135 RGBA8 filledWithZeros(0, 0, 0, 0);
136 EXPECT_LAZY_CLEAR(1u, EXPECT_PIXEL_RGBA8_EQ(filledWithZeros, texture, 0, 0));
137
138 // Expect texture subresource initialized to be true
139 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
140 }
141
142 // This tests that the code path of CopyTextureToBuffer with multiple texture array layers clears
143 // correctly to Zero after first usage
TEST_P(TextureZeroInitTest,CopyMultipleTextureArrayLayersToBufferSource)144 TEST_P(TextureZeroInitTest, CopyMultipleTextureArrayLayersToBufferSource) {
145 constexpr uint32_t kArrayLayers = 6u;
146
147 const wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
148 1, kArrayLayers, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
149 kColorFormat);
150 wgpu::Texture texture = device.CreateTexture(&descriptor);
151
152 const uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(kColorFormat, kSize);
153 const uint32_t rowsPerImage = kSize;
154 wgpu::BufferDescriptor bufferDescriptor;
155 bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
156 bufferDescriptor.size = utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage,
157 {kSize, kSize, kArrayLayers}, kColorFormat);
158 wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
159
160 const wgpu::ImageCopyBuffer imageCopyBuffer =
161 utils::CreateImageCopyBuffer(buffer, 0, bytesPerRow, kSize);
162 const wgpu::ImageCopyTexture imageCopyTexture =
163 utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
164 const wgpu::Extent3D copySize = {kSize, kSize, kArrayLayers};
165
166 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
167 encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size);
168 wgpu::CommandBuffer commandBuffer = encoder.Finish();
169
170 // Expect texture to be lazy initialized.
171 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer));
172
173 // Expect texture subresource initialized to be true
174 EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, kArrayLayers));
175
176 const std::vector<RGBA8> kExpectedAllZero(kSize * kSize, {0, 0, 0, 0});
177 for (uint32_t layer = 0; layer < kArrayLayers; ++layer) {
178 EXPECT_TEXTURE_EQ(kExpectedAllZero.data(), texture, {0, 0, layer}, {kSize, kSize});
179 }
180 }
181
182 // Test that non-zero mip level clears subresource to Zero after first use
183 // This goes through the BeginRenderPass's code path
TEST_P(TextureZeroInitTest,RenderingMipMapClearsToZero)184 TEST_P(TextureZeroInitTest, RenderingMipMapClearsToZero) {
185 uint32_t baseMipLevel = 2;
186 uint32_t levelCount = 4;
187 uint32_t baseArrayLayer = 0;
188 uint32_t layerCount = 1;
189
190 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
191 levelCount, layerCount, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
192 kColorFormat);
193 wgpu::Texture texture = device.CreateTexture(&descriptor);
194
195 wgpu::TextureViewDescriptor viewDescriptor =
196 CreateTextureViewDescriptor(baseMipLevel, baseArrayLayer);
197 wgpu::TextureView view = texture.CreateView(&viewDescriptor);
198
199 utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
200
201 // Specify loadOp Load. Clear should be used to zero-initialize.
202 renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
203 // Specify non-zero clear color. It should still be cleared to zero.
204 renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f};
205 renderPass.renderPassInfo.cColorAttachments[0].view = view;
206
207 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
208 {
209 // Texture's first usage is in BeginRenderPass's call to RecordRenderPass
210 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
211 pass.EndPass();
212 }
213 wgpu::CommandBuffer commands = encoder.Finish();
214 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
215
216 uint32_t mipSize = kSize >> 2;
217 std::vector<RGBA8> expected(mipSize * mipSize, {0, 0, 0, 0});
218
219 EXPECT_TEXTURE_EQ(expected.data(), renderPass.color, {0, 0, baseArrayLayer}, {mipSize, mipSize},
220 baseMipLevel);
221
222 // Expect texture subresource initialized to be true
223 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
224 renderPass.color.Get(), baseMipLevel, 1, baseArrayLayer, 1));
225 }
226
227 // Test that non-zero array layers clears subresource to Zero after first use.
228 // This goes through the BeginRenderPass's code path
TEST_P(TextureZeroInitTest,RenderingArrayLayerClearsToZero)229 TEST_P(TextureZeroInitTest, RenderingArrayLayerClearsToZero) {
230 uint32_t baseMipLevel = 0;
231 uint32_t levelCount = 1;
232 uint32_t baseArrayLayer = 2;
233 uint32_t layerCount = 4;
234
235 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
236 levelCount, layerCount, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
237 kColorFormat);
238 wgpu::Texture texture = device.CreateTexture(&descriptor);
239
240 wgpu::TextureViewDescriptor viewDescriptor =
241 CreateTextureViewDescriptor(baseMipLevel, baseArrayLayer);
242 wgpu::TextureView view = texture.CreateView(&viewDescriptor);
243
244 utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
245
246 // Specify loadOp Load. Clear should be used to zero-initialize.
247 renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
248 // Specify non-zero clear color. It should still be cleared to zero.
249 renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f};
250 renderPass.renderPassInfo.cColorAttachments[0].view = view;
251
252 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
253 {
254 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
255 pass.EndPass();
256 }
257 wgpu::CommandBuffer commands = encoder.Finish();
258 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
259
260 std::vector<RGBA8> expected(kSize * kSize, {0, 0, 0, 0});
261
262 EXPECT_TEXTURE_EQ(expected.data(), renderPass.color, {0, 0, baseArrayLayer}, {kSize, kSize},
263 baseMipLevel);
264
265 // Expect texture subresource initialized to be true
266 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
267 renderPass.color.Get(), baseMipLevel, 1, baseArrayLayer, 1));
268 }
269
270 // This tests CopyBufferToTexture fully overwrites copy so lazy init is not needed.
TEST_P(TextureZeroInitTest,CopyBufferToTexture)271 TEST_P(TextureZeroInitTest, CopyBufferToTexture) {
272 wgpu::TextureDescriptor descriptor =
273 CreateTextureDescriptor(4, 1,
274 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
275 wgpu::TextureUsage::CopySrc,
276 kColorFormat);
277 wgpu::Texture texture = device.CreateTexture(&descriptor);
278
279 std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 100);
280 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
281 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
282
283 wgpu::ImageCopyBuffer imageCopyBuffer =
284 utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * sizeof(uint32_t));
285 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
286 wgpu::Extent3D copySize = {kSize, kSize, 1};
287
288 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
289 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
290 wgpu::CommandBuffer commands = encoder.Finish();
291 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
292
293 std::vector<RGBA8> expected(kSize * kSize, {100, 100, 100, 100});
294
295 EXPECT_TEXTURE_EQ(expected.data(), texture, {0, 0}, {kSize, kSize});
296
297 // Expect texture subresource initialized to be true
298 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
299 }
300
301 // Test for a copy only to a subset of the subresource, lazy init is necessary to clear the other
302 // half.
TEST_P(TextureZeroInitTest,CopyBufferToTextureHalf)303 TEST_P(TextureZeroInitTest, CopyBufferToTextureHalf) {
304 wgpu::TextureDescriptor descriptor =
305 CreateTextureDescriptor(4, 1,
306 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
307 wgpu::TextureUsage::CopySrc,
308 kColorFormat);
309 wgpu::Texture texture = device.CreateTexture(&descriptor);
310
311 std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 100);
312 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
313 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
314
315 wgpu::ImageCopyBuffer imageCopyBuffer =
316 utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * sizeof(uint16_t));
317 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
318 wgpu::Extent3D copySize = {kSize / 2, kSize, 1};
319
320 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
321 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
322 wgpu::CommandBuffer commands = encoder.Finish();
323 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
324
325 std::vector<RGBA8> expected100((kSize / 2) * kSize, {100, 100, 100, 100});
326 std::vector<RGBA8> expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0});
327 // first half filled with 100, by the buffer data
328 EXPECT_TEXTURE_EQ(expected100.data(), texture, {0, 0}, {kSize / 2, kSize});
329 // second half should be cleared
330 EXPECT_TEXTURE_EQ(expectedZeros.data(), texture, {kSize / 2, 0}, {kSize / 2, kSize});
331
332 // Expect texture subresource initialized to be true
333 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
334 }
335
336 // This tests CopyBufferToTexture fully overwrites a range of subresources, so lazy initialization
337 // is needed for neither the subresources involved in the copy nor the other subresources.
TEST_P(TextureZeroInitTest,CopyBufferToTextureMultipleArrayLayers)338 TEST_P(TextureZeroInitTest, CopyBufferToTextureMultipleArrayLayers) {
339 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
340 1, 6, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat);
341 wgpu::Texture texture = device.CreateTexture(&descriptor);
342
343 constexpr uint32_t kBaseArrayLayer = 2u;
344 constexpr uint32_t kCopyLayerCount = 3u;
345 std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize * kCopyLayerCount, 100);
346 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
347 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
348
349 const wgpu::ImageCopyBuffer imageCopyBuffer =
350 utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize, kSize);
351 const wgpu::ImageCopyTexture imageCopyTexture =
352 utils::CreateImageCopyTexture(texture, 0, {0, 0, kBaseArrayLayer});
353 const wgpu::Extent3D copySize = {kSize, kSize, kCopyLayerCount};
354
355 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
356 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
357 wgpu::CommandBuffer commands = encoder.Finish();
358
359 // The copy overwrites the whole subresources so we don't need to do lazy initialization on
360 // them.
361 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
362
363 // Expect texture subresource initialized to be true
364 EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, kBaseArrayLayer,
365 kCopyLayerCount));
366
367 const std::vector<RGBA8> expected100(kSize * kSize, {100, 100, 100, 100});
368 for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) {
369 EXPECT_TEXTURE_EQ(expected100.data(), texture, {0, 0, layer}, {kSize, kSize});
370 }
371 }
372
373 // This tests CopyTextureToTexture fully overwrites copy so lazy init is not needed.
TEST_P(TextureZeroInitTest,CopyTextureToTexture)374 TEST_P(TextureZeroInitTest, CopyTextureToTexture) {
375 wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor(
376 1, 1, wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc, kColorFormat);
377 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
378
379 wgpu::ImageCopyTexture srcImageCopyTexture =
380 utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0});
381
382 wgpu::TextureDescriptor dstDescriptor =
383 CreateTextureDescriptor(1, 1,
384 wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst |
385 wgpu::TextureUsage::CopySrc,
386 kColorFormat);
387 wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor);
388
389 wgpu::ImageCopyTexture dstImageCopyTexture =
390 utils::CreateImageCopyTexture(dstTexture, 0, {0, 0, 0});
391
392 wgpu::Extent3D copySize = {kSize, kSize, 1};
393
394 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
395 encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Size);
396 wgpu::CommandBuffer commands = encoder.Finish();
397 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
398
399 std::vector<RGBA8> expected(kSize * kSize, {0, 0, 0, 0});
400
401 EXPECT_TEXTURE_EQ(expected.data(), srcTexture, {0, 0}, {kSize, kSize});
402 EXPECT_TEXTURE_EQ(expected.data(), dstTexture, {0, 0}, {kSize, kSize});
403
404 // Expect texture subresource initialized to be true
405 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1));
406 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(dstTexture.Get(), 0, 1, 0, 1));
407 }
408
409 // This Tests the CopyTextureToTexture's copy only to a subset of the subresource, lazy init is
410 // necessary to clear the other half.
TEST_P(TextureZeroInitTest,CopyTextureToTextureHalf)411 TEST_P(TextureZeroInitTest, CopyTextureToTextureHalf) {
412 wgpu::TextureDescriptor srcDescriptor =
413 CreateTextureDescriptor(1, 1,
414 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc |
415 wgpu::TextureUsage::CopyDst,
416 kColorFormat);
417 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
418
419 // fill srcTexture with 100
420 {
421 std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 100);
422 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
423 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
424 wgpu::ImageCopyBuffer imageCopyBuffer =
425 utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize);
426 wgpu::ImageCopyTexture imageCopyTexture =
427 utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0});
428 wgpu::Extent3D copySize = {kSize, kSize, 1};
429 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
430 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
431 wgpu::CommandBuffer commands = encoder.Finish();
432 queue.Submit(1, &commands);
433 }
434
435 wgpu::ImageCopyTexture srcImageCopyTexture =
436 utils::CreateImageCopyTexture(srcTexture, 0, {0, 0, 0});
437
438 wgpu::TextureDescriptor dstDescriptor =
439 CreateTextureDescriptor(1, 1,
440 wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopyDst |
441 wgpu::TextureUsage::CopySrc,
442 kColorFormat);
443 wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor);
444
445 wgpu::ImageCopyTexture dstImageCopyTexture =
446 utils::CreateImageCopyTexture(dstTexture, 0, {0, 0, 0});
447 wgpu::Extent3D copySize = {kSize / 2, kSize, 1};
448
449 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
450 encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Size);
451 wgpu::CommandBuffer commands = encoder.Finish();
452 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
453
454 std::vector<RGBA8> expectedWithZeros((kSize / 2) * kSize, {0, 0, 0, 0});
455 std::vector<RGBA8> expectedWith100(kSize * kSize, {100, 100, 100, 100});
456
457 EXPECT_TEXTURE_EQ(expectedWith100.data(), srcTexture, {0, 0}, {kSize, kSize});
458 EXPECT_TEXTURE_EQ(expectedWith100.data(), dstTexture, {0, 0}, {kSize / 2, kSize});
459 EXPECT_TEXTURE_EQ(expectedWithZeros.data(), dstTexture, {kSize / 2, 0}, {kSize / 2, kSize});
460
461 // Expect texture subresource initialized to be true
462 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1));
463 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(dstTexture.Get(), 0, 1, 0, 1));
464 }
465
466 // This tests the texture with depth attachment and load op load will init depth stencil texture to
467 // 0s.
TEST_P(TextureZeroInitTest,RenderingLoadingDepth)468 TEST_P(TextureZeroInitTest, RenderingLoadingDepth) {
469 wgpu::TextureDescriptor srcDescriptor =
470 CreateTextureDescriptor(1, 1,
471 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
472 wgpu::TextureUsage::RenderAttachment,
473 kColorFormat);
474 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
475
476 wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor(
477 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
478 kDepthStencilFormat);
479 wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor);
480
481 utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
482 depthStencilTexture.CreateView());
483 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
484 // Set clearDepth to non-zero. It should still be cleared to 0 by the loadOp.
485 renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.5f;
486 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
487 renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0;
488 renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
489 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
490
491 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
492 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
493 pass.SetPipeline(CreatePipelineForTest());
494 pass.Draw(6);
495 pass.EndPass();
496 wgpu::CommandBuffer commandBuffer = encoder.Finish();
497 // Expect 0 lazy clears, depth stencil texture will clear using loadop
498 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
499
500 // Expect the texture to be red because depth test passed.
501 std::vector<RGBA8> expected(kSize * kSize, {255, 0, 0, 255});
502 EXPECT_TEXTURE_EQ(expected.data(), srcTexture, {0, 0}, {kSize, kSize});
503
504 // Expect texture subresource initialized to be true
505 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1));
506 }
507
508 // This tests the texture with stencil attachment and load op load will init depth stencil texture
509 // to 0s.
TEST_P(TextureZeroInitTest,RenderingLoadingStencil)510 TEST_P(TextureZeroInitTest, RenderingLoadingStencil) {
511 wgpu::TextureDescriptor srcDescriptor =
512 CreateTextureDescriptor(1, 1,
513 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
514 wgpu::TextureUsage::RenderAttachment,
515 kColorFormat);
516 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
517
518 wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor(
519 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
520 kDepthStencilFormat);
521 wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor);
522
523 utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
524 depthStencilTexture.CreateView());
525 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
526 renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f;
527 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
528 // Set clearStencil to non-zero. It should still be cleared to 0 by the loadOp.
529 renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2;
530 renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
531 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
532
533 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
534 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
535 pass.SetPipeline(CreatePipelineForTest());
536 pass.Draw(6);
537 pass.EndPass();
538 wgpu::CommandBuffer commandBuffer = encoder.Finish();
539 // Expect 0 lazy clears, depth stencil texture will clear using loadop
540 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
541
542 // Expect the texture to be red because stencil test passed.
543 std::vector<RGBA8> expected(kSize * kSize, {255, 0, 0, 255});
544 EXPECT_TEXTURE_EQ(expected.data(), srcTexture, {0, 0}, {kSize, kSize});
545
546 // Expect texture subresource initialized to be true
547 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1));
548 }
549
550 // This tests the texture with depth stencil attachment and load op load will init depth stencil
551 // texture to 0s.
TEST_P(TextureZeroInitTest,RenderingLoadingDepthStencil)552 TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencil) {
553 wgpu::TextureDescriptor srcDescriptor =
554 CreateTextureDescriptor(1, 1,
555 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
556 wgpu::TextureUsage::RenderAttachment,
557 kColorFormat);
558 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
559
560 wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor(
561 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
562 kDepthStencilFormat);
563 wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor);
564
565 utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
566 depthStencilTexture.CreateView());
567 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
568 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
569 renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
570 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
571
572 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
573 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
574 pass.SetPipeline(CreatePipelineForTest());
575 pass.Draw(6);
576 pass.EndPass();
577 wgpu::CommandBuffer commandBuffer = encoder.Finish();
578 // Expect 0 lazy clears, depth stencil texture will clear using loadop
579 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
580
581 // Expect the texture to be red because both depth and stencil tests passed.
582 std::vector<RGBA8> expected(kSize * kSize, {255, 0, 0, 255});
583 EXPECT_TEXTURE_EQ(expected.data(), srcTexture, {0, 0}, {kSize, kSize});
584
585 // Expect texture subresource initialized to be true
586 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1));
587 }
588
589 // Test that clear state is tracked independently for depth/stencil textures.
TEST_P(TextureZeroInitTest,IndependentDepthStencilLoadAfterDiscard)590 TEST_P(TextureZeroInitTest, IndependentDepthStencilLoadAfterDiscard) {
591 // TODO(crbug.com/dawn/704): Readback after clear via stencil copy does not work
592 // on some Intel drivers.
593 DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
594
595 // TODO(crbug.com/dawn/1151): The test started failing on Wintel Vulkan when Discard was
596 // implemented for the Vulkan backend.
597 DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsWindows() && IsIntel());
598
599 wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor(
600 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
601 kDepthStencilFormat);
602 wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor);
603
604 // Uninitialize only depth
605 {
606 // Clear the stencil to 2 and discard the depth
607 {
608 utils::ComboRenderPassDescriptor renderPassDescriptor({},
609 depthStencilTexture.CreateView());
610 renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Discard;
611 renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2;
612 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
613
614 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
615 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
616 pass.EndPass();
617 wgpu::CommandBuffer commandBuffer = encoder.Finish();
618 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
619 }
620
621 // "all" subresources are not initialized; Depth is not initialized
622 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(
623 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All));
624 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(
625 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly));
626 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
627 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
628
629 // Now load both depth and stencil. Depth should be cleared and stencil should stay the same
630 // at 2.
631 {
632 wgpu::TextureDescriptor colorDescriptor =
633 CreateTextureDescriptor(1, 1,
634 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
635 wgpu::TextureUsage::RenderAttachment,
636 kColorFormat);
637 wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor);
638
639 utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()},
640 depthStencilTexture.CreateView());
641 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
642 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
643
644 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
645 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
646 pass.SetPipeline(CreatePipelineForTest());
647 pass.SetStencilReference(2);
648 pass.Draw(6);
649 pass.EndPass();
650 wgpu::CommandBuffer commandBuffer = encoder.Finish();
651 // No lazy clear because depth will be cleared with a loadOp
652 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
653
654 // Expect the texture to be red because the depth and stencil tests passed. Depth was 0
655 // and stencil was 2.
656 std::vector<RGBA8> expected(kSize * kSize, {255, 0, 0, 255});
657 EXPECT_TEXTURE_EQ(expected.data(), colorTexture, {0, 0}, {kSize, kSize});
658 }
659
660 // Everything is initialized now
661 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
662 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All));
663 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
664 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly));
665 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
666 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
667
668 // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms
669 if (IsMetal() || IsVulkan() || IsD3D12()) {
670 // Check by copy that the stencil data is 2.
671 std::vector<uint8_t> expected(kSize * kSize, 2);
672 EXPECT_LAZY_CLEAR(
673 0u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, {0, 0}, {kSize, kSize},
674 0, wgpu::TextureAspect::StencilOnly));
675 }
676 }
677
678 // Uninitialize only stencil
679 {
680 // Clear the depth to 0.7 and discard the stencil.
681 {
682 utils::ComboRenderPassDescriptor renderPassDescriptor({},
683 depthStencilTexture.CreateView());
684 renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.7;
685 renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
686 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp =
687 wgpu::StoreOp::Discard;
688
689 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
690 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
691 pass.EndPass();
692 wgpu::CommandBuffer commandBuffer = encoder.Finish();
693 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
694 }
695
696 // "all" subresources are not initialized; Stencil is not initialized
697 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(
698 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All));
699 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
700 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly));
701 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(
702 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
703
704 // Now load both depth and stencil. Stencil should be cleared and depth should stay the same
705 // at 0.7.
706 {
707 wgpu::TextureDescriptor colorDescriptor =
708 CreateTextureDescriptor(1, 1,
709 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
710 wgpu::TextureUsage::RenderAttachment,
711 kColorFormat);
712 wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor);
713
714 utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()},
715 depthStencilTexture.CreateView());
716 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
717 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
718
719 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
720 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
721 pass.SetPipeline(CreatePipelineForTest(0.7));
722 pass.Draw(6);
723 pass.EndPass();
724 wgpu::CommandBuffer commandBuffer = encoder.Finish();
725 // No lazy clear because stencil will clear using a loadOp.
726 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
727
728 // Expect the texture to be red because both the depth a stencil tests passed.
729 // Depth was 0.7 and stencil was 0
730 std::vector<RGBA8> expected(kSize * kSize, {255, 0, 0, 255});
731 EXPECT_TEXTURE_EQ(expected.data(), colorTexture, {0, 0}, {kSize, kSize});
732 }
733
734 // Everything is initialized now
735 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
736 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_All));
737 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
738 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_DepthOnly));
739 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(
740 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
741
742 // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms
743 if (IsMetal() || IsVulkan() || IsD3D12()) {
744 // Check by copy that the stencil data is 0.
745 std::vector<uint8_t> expected(kSize * kSize, 0);
746 EXPECT_LAZY_CLEAR(
747 0u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, {0, 0}, {kSize, kSize},
748 0, wgpu::TextureAspect::StencilOnly));
749 }
750 }
751 }
752
753 // Test that clear state is tracked independently for depth/stencil textures.
754 // Lazy clear of the stencil aspect via copy should not touch depth.
TEST_P(TextureZeroInitTest,IndependentDepthStencilCopyAfterDiscard)755 TEST_P(TextureZeroInitTest, IndependentDepthStencilCopyAfterDiscard) {
756 // TODO(crbug.com/dawn/439): Implement stencil copies on other platforms
757 DAWN_SUPPRESS_TEST_IF(!(IsMetal() || IsVulkan() || IsD3D12()));
758
759 // TODO(enga): Figure out why this fails on Metal Intel.
760 DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
761
762 wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor(
763 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc,
764 kDepthStencilFormat);
765 wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor);
766
767 // Clear the depth to 0.3 and discard the stencil.
768 {
769 utils::ComboRenderPassDescriptor renderPassDescriptor({}, depthStencilTexture.CreateView());
770 renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.3;
771 renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
772 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Discard;
773
774 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
775 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
776 pass.EndPass();
777 wgpu::CommandBuffer commandBuffer = encoder.Finish();
778 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
779 }
780
781 // "all" subresources are not initialized; Stencil is not initialized
782 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1,
783 0, 1, WGPUTextureAspect_All));
784 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0,
785 1, WGPUTextureAspect_DepthOnly));
786 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(
787 depthStencilTexture.Get(), 0, 1, 0, 1, WGPUTextureAspect_StencilOnly));
788
789 // Check by copy that the stencil data is lazily cleared to 0.
790 std::vector<uint8_t> expected(kSize * kSize, 0);
791 EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_EQ(expected.data(), depthStencilTexture, {0, 0},
792 {kSize, kSize}, 0, wgpu::TextureAspect::StencilOnly));
793
794 // Everything is initialized now
795 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0,
796 1, WGPUTextureAspect_All));
797 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0,
798 1, WGPUTextureAspect_DepthOnly));
799 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, 1, 0,
800 1, WGPUTextureAspect_StencilOnly));
801
802 // Now load both depth and stencil. Stencil should be cleared and depth should stay the same
803 // at 0.3.
804 {
805 wgpu::TextureDescriptor colorDescriptor =
806 CreateTextureDescriptor(1, 1,
807 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
808 wgpu::TextureUsage::RenderAttachment,
809 kColorFormat);
810 wgpu::Texture colorTexture = device.CreateTexture(&colorDescriptor);
811
812 utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()},
813 depthStencilTexture.CreateView());
814 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
815 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
816
817 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
818 auto pass = encoder.BeginRenderPass(&renderPassDescriptor);
819 pass.SetPipeline(CreatePipelineForTest(0.3));
820 pass.Draw(6);
821 pass.EndPass();
822 wgpu::CommandBuffer commandBuffer = encoder.Finish();
823 // No lazy clear because stencil will clear using a loadOp.
824 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
825
826 // Expect the texture to be red because both the depth a stencil tests passed.
827 // Depth was 0.3 and stencil was 0
828 std::vector<RGBA8> expected(kSize * kSize, {255, 0, 0, 255});
829 EXPECT_TEXTURE_EQ(expected.data(), colorTexture, {0, 0}, {kSize, kSize});
830 }
831 }
832
833 // This tests the color attachments clear to 0s
TEST_P(TextureZeroInitTest,ColorAttachmentsClear)834 TEST_P(TextureZeroInitTest, ColorAttachmentsClear) {
835 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
836 1, 1, wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc, kColorFormat);
837 wgpu::Texture texture = device.CreateTexture(&descriptor);
838 utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
839 renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
840
841 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
842 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
843 pass.EndPass();
844
845 wgpu::CommandBuffer commands = encoder.Finish();
846 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
847
848 std::vector<RGBA8> expected(kSize * kSize, {0, 0, 0, 0});
849 EXPECT_TEXTURE_EQ(expected.data(), renderPass.color, {0, 0}, {kSize, kSize});
850
851 // Expect texture subresource initialized to be true
852 EXPECT_EQ(true,
853 dawn_native::IsTextureSubresourceInitialized(renderPass.color.Get(), 0, 1, 0, 1));
854 }
855
856 // This tests the clearing of sampled textures in render pass
TEST_P(TextureZeroInitTest,RenderPassSampledTextureClear)857 TEST_P(TextureZeroInitTest, RenderPassSampledTextureClear) {
858 // Create needed resources
859 wgpu::TextureDescriptor descriptor =
860 CreateTextureDescriptor(1, 1, wgpu::TextureUsage::TextureBinding, kColorFormat);
861 wgpu::Texture texture = device.CreateTexture(&descriptor);
862
863 wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
864 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat);
865 wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
866
867 // Create render pipeline
868 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
869 renderPipelineDescriptor.cTargets[0].format = kColorFormat;
870 renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
871 renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest();
872 wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
873
874 // Create bindgroup
875 wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
876 {{0, texture.CreateView()}});
877
878 // Encode pass and submit
879 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
880 utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
881 renderPassDesc.cColorAttachments[0].clearColor = {1.0, 1.0, 1.0, 1.0};
882 renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
883 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
884 pass.SetPipeline(renderPipeline);
885 pass.SetBindGroup(0, bindGroup);
886 pass.Draw(6);
887 pass.EndPass();
888 wgpu::CommandBuffer commands = encoder.Finish();
889 // Expect 1 lazy clear for sampled texture
890 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
891
892 // Expect the rendered texture to be cleared
893 std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
894 EXPECT_TEXTURE_EQ(expectedWithZeros.data(), renderTexture, {0, 0}, {kSize, kSize});
895
896 // Expect texture subresource initialized to be true
897 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1));
898 }
899
900 // This is a regression test for a bug where a texture wouldn't get clear for a pass if at least
901 // one of its subresources was used as an attachment. It tests that if a texture is used as both
902 // sampled and attachment (with LoadOp::Clear so the lazy clear can be skipped) then the sampled
903 // subresource is correctly cleared.
TEST_P(TextureZeroInitTest,TextureBothSampledAndAttachmentClear)904 TEST_P(TextureZeroInitTest, TextureBothSampledAndAttachmentClear) {
905 // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES.
906 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
907
908 // Create a 2D array texture, layer 0 will be used as attachment, layer 1 as sampled.
909 wgpu::TextureDescriptor texDesc;
910 texDesc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::RenderAttachment |
911 wgpu::TextureUsage::CopySrc;
912 texDesc.size = {1, 1, 2};
913 texDesc.format = wgpu::TextureFormat::RGBA8Unorm;
914 wgpu::Texture texture = device.CreateTexture(&texDesc);
915
916 wgpu::TextureViewDescriptor viewDesc;
917 viewDesc.dimension = wgpu::TextureViewDimension::e2D;
918 viewDesc.arrayLayerCount = 1;
919
920 viewDesc.baseArrayLayer = 0;
921 wgpu::TextureView attachmentView = texture.CreateView(&viewDesc);
922
923 viewDesc.baseArrayLayer = 1;
924 wgpu::TextureView sampleView = texture.CreateView(&viewDesc);
925
926 // Create render pipeline
927 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
928 renderPipelineDescriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
929 renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
930 renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest();
931 wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
932
933 wgpu::BindGroup bindGroup =
934 utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), {{0, sampleView}});
935
936 // Encode pass and submit
937 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
938 utils::ComboRenderPassDescriptor renderPassDesc({attachmentView});
939 renderPassDesc.cColorAttachments[0].clearColor = {1.0, 1.0, 1.0, 1.0};
940 renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
941 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
942 pass.SetPipeline(renderPipeline);
943 pass.SetBindGroup(0, bindGroup);
944 pass.Draw(6);
945 pass.EndPass();
946 wgpu::CommandBuffer commands = encoder.Finish();
947
948 // Expect the lazy clear for the sampled subresource.
949 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
950
951 // Expect both subresources to be zero: the sampled one with lazy-clearing and the attachment
952 // because it sampled the lazy-cleared sampled subresource.
953 EXPECT_TEXTURE_EQ(&RGBA8::kZero, texture, {0, 0, 0}, {1, 1});
954 EXPECT_TEXTURE_EQ(&RGBA8::kZero, texture, {0, 0, 1}, {1, 1});
955
956 // The whole texture is now initialized.
957 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 2));
958 }
959
960 // This tests the clearing of sampled textures during compute pass
TEST_P(TextureZeroInitTest,ComputePassSampledTextureClear)961 TEST_P(TextureZeroInitTest, ComputePassSampledTextureClear) {
962 // Create needed resources
963 wgpu::TextureDescriptor descriptor =
964 CreateTextureDescriptor(1, 1, wgpu::TextureUsage::TextureBinding, kColorFormat);
965 descriptor.size.width = 1;
966 descriptor.size.height = 1;
967 wgpu::Texture texture = device.CreateTexture(&descriptor);
968
969 uint32_t bufferSize = kFormatBlockByteSize * sizeof(uint32_t);
970 wgpu::BufferDescriptor bufferDescriptor;
971 bufferDescriptor.size = bufferSize;
972 bufferDescriptor.usage =
973 wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst;
974 wgpu::Buffer bufferTex = device.CreateBuffer(&bufferDescriptor);
975 // Add data to buffer to ensure it is initialized
976 uint32_t data = 100;
977 queue.WriteBuffer(bufferTex, 0, &data, sizeof(data));
978
979 wgpu::Sampler sampler = device.CreateSampler();
980
981 // Create compute pipeline
982 wgpu::ComputePipelineDescriptor computePipelineDescriptor;
983 wgpu::ProgrammableStageDescriptor compute;
984 const char* cs = R"(
985 [[group(0), binding(0)]] var tex : texture_2d<f32>;
986 [[block]] struct Result {
987 value : vec4<f32>;
988 };
989 [[group(0), binding(1)]] var<storage, read_write> result : Result;
990 [[stage(compute), workgroup_size(1)]] fn main() {
991 result.value = textureLoad(tex, vec2<i32>(0,0), 0);
992 }
993 )";
994 computePipelineDescriptor.compute.module = utils::CreateShaderModule(device, cs);
995 computePipelineDescriptor.compute.entryPoint = "main";
996 wgpu::ComputePipeline computePipeline =
997 device.CreateComputePipeline(&computePipelineDescriptor);
998
999 // Create bindgroup
1000 wgpu::BindGroup bindGroup =
1001 utils::MakeBindGroup(device, computePipeline.GetBindGroupLayout(0),
1002 {{0, texture.CreateView()}, {1, bufferTex, 0, bufferSize}});
1003
1004 // Encode the pass and submit
1005 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1006 wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
1007 pass.SetPipeline(computePipeline);
1008 pass.SetBindGroup(0, bindGroup);
1009 pass.Dispatch(1);
1010 pass.EndPass();
1011 wgpu::CommandBuffer commands = encoder.Finish();
1012 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
1013
1014 // Expect the buffer to be zeroed out by the compute pass
1015 std::vector<uint32_t> expectedWithZeros(bufferSize, 0);
1016 EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferTex, 0, kFormatBlockByteSize);
1017
1018 // Expect texture subresource initialized to be true
1019 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
1020 }
1021
1022 // This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures
TEST_P(TextureZeroInitTest,NonRenderableTextureClear)1023 TEST_P(TextureZeroInitTest, NonRenderableTextureClear) {
1024 // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading
1025 // from Snorm textures.
1026 DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_snorm_read"));
1027
1028 wgpu::TextureDescriptor descriptor =
1029 CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat);
1030 wgpu::Texture texture = device.CreateTexture(&descriptor);
1031
1032 // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy
1033 uint32_t bytesPerRow = Align(kSize * kFormatBlockByteSize, kTextureBytesPerRowAlignment);
1034 uint32_t bufferSize = bytesPerRow * kSize;
1035 std::vector<uint8_t> data(bufferSize, 100);
1036 wgpu::Buffer bufferDst = utils::CreateBufferFromData(
1037 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
1038
1039 wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(bufferDst, 0, bytesPerRow);
1040 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
1041 wgpu::Extent3D copySize = {kSize, kSize, 1};
1042
1043 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1044 encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size);
1045 wgpu::CommandBuffer commands = encoder.Finish();
1046 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
1047
1048 std::vector<uint32_t> expectedWithZeros(bufferSize, 0);
1049 EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, kSize);
1050
1051 // Expect texture subresource initialized to be true
1052 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
1053 }
1054
1055 // This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures
TEST_P(TextureZeroInitTest,NonRenderableTextureClearUnalignedSize)1056 TEST_P(TextureZeroInitTest, NonRenderableTextureClearUnalignedSize) {
1057 // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading
1058 // from Snorm textures.
1059 DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_snorm_read"));
1060
1061 wgpu::TextureDescriptor descriptor =
1062 CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat);
1063 descriptor.size.width = kUnalignedSize;
1064 descriptor.size.height = kUnalignedSize;
1065 wgpu::Texture texture = device.CreateTexture(&descriptor);
1066
1067 // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy
1068 uint32_t bytesPerRow =
1069 Align(kUnalignedSize * kFormatBlockByteSize, kTextureBytesPerRowAlignment);
1070 uint32_t bufferSize = bytesPerRow * kUnalignedSize;
1071 std::vector<uint8_t> data(bufferSize, 100);
1072 wgpu::Buffer bufferDst = utils::CreateBufferFromData(
1073 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
1074 wgpu::ImageCopyBuffer imageCopyBuffer = utils::CreateImageCopyBuffer(bufferDst, 0, bytesPerRow);
1075 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
1076 wgpu::Extent3D copySize = {kUnalignedSize, kUnalignedSize, 1};
1077
1078 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1079 encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size);
1080 wgpu::CommandBuffer commands = encoder.Finish();
1081 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
1082
1083 std::vector<uint32_t> expectedWithZeros(bufferSize, 0);
1084 EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, kUnalignedSize);
1085
1086 // Expect texture subresource initialized to be true
1087 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
1088 }
1089
1090 // This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures
1091 // with more than 1 array layers
TEST_P(TextureZeroInitTest,NonRenderableTextureClearWithMultiArrayLayers)1092 TEST_P(TextureZeroInitTest, NonRenderableTextureClearWithMultiArrayLayers) {
1093 // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading
1094 // from Snorm textures.
1095 DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_snorm_read"));
1096
1097 wgpu::TextureDescriptor descriptor =
1098 CreateTextureDescriptor(1, 2, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat);
1099 wgpu::Texture texture = device.CreateTexture(&descriptor);
1100
1101 // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy
1102 uint32_t bufferSize = kFormatBlockByteSize * kSize * kSize;
1103 std::vector<uint8_t> data(bufferSize, 100);
1104 wgpu::Buffer bufferDst = utils::CreateBufferFromData(
1105 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
1106
1107 wgpu::ImageCopyBuffer imageCopyBuffer =
1108 utils::CreateImageCopyBuffer(bufferDst, 0, kSize * kFormatBlockByteSize);
1109 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 1});
1110 wgpu::Extent3D copySize = {kSize, kSize, 1};
1111
1112 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1113 encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size);
1114 wgpu::CommandBuffer commands = encoder.Finish();
1115 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
1116
1117 std::vector<uint32_t> expectedWithZeros(bufferSize, 0);
1118 EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8);
1119
1120 // Expect texture subresource initialized to be true
1121 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 1, 1));
1122 }
1123
1124 // This tests that storeOp clear resets resource state to uninitialized.
1125 // Start with a sample texture that is initialized with data.
1126 // Then expect the render texture to not store the data from sample texture
1127 // because it will be lazy cleared by the EXPECT_TEXTURE_EQ call.
TEST_P(TextureZeroInitTest,RenderPassStoreOpClear)1128 TEST_P(TextureZeroInitTest, RenderPassStoreOpClear) {
1129 // Create needed resources
1130 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
1131 1, 1, wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst, kColorFormat);
1132 wgpu::Texture texture = device.CreateTexture(&descriptor);
1133
1134 wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
1135 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat);
1136 wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
1137
1138 // Fill the sample texture with data
1139 std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 1);
1140 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
1141 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
1142 wgpu::ImageCopyBuffer imageCopyBuffer =
1143 utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize);
1144 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
1145 wgpu::Extent3D copySize = {kSize, kSize, 1};
1146 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1147 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
1148 wgpu::CommandBuffer commands = encoder.Finish();
1149 // Expect 0 lazy clears because the texture will be completely copied to
1150 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
1151
1152 // Create render pipeline
1153 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
1154 renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
1155 renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest();
1156 renderPipelineDescriptor.cTargets[0].format = kColorFormat;
1157 wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
1158
1159 // Create bindgroup
1160 wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
1161 {{0, texture.CreateView()}});
1162
1163 // Encode pass and submit
1164 encoder = device.CreateCommandEncoder();
1165 utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
1166 renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
1167 renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
1168 renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
1169 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
1170 pass.SetPipeline(renderPipeline);
1171 pass.SetBindGroup(0, bindGroup);
1172 pass.Draw(6);
1173 pass.EndPass();
1174 commands = encoder.Finish();
1175 // Expect 0 lazy clears, sample texture is initialized by copyBufferToTexture and render texture
1176 // is cleared by loadop
1177 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
1178
1179 // Expect the rendered texture to be cleared
1180 std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
1181 EXPECT_LAZY_CLEAR(
1182 1u, EXPECT_TEXTURE_EQ(expectedWithZeros.data(), renderTexture, {0, 0}, {kSize, kSize}));
1183
1184 // Expect texture subresource initialized to be true
1185 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
1186 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1));
1187 }
1188
1189 // This tests storeOp Clear on depth and stencil textures.
1190 // We put the depth stencil texture through 2 passes:
1191 // 1) LoadOp::Clear and StoreOp::Discard, fail the depth and stencil test set in the render
1192 // pipeline. This means nothing is drawn and subresource is set as uninitialized.
1193 // 2) LoadOp::Load and StoreOp::Discard, pass the depth and stencil test set in the render pipeline.
1194 // Because LoadOp is Load and the subresource is uninitialized, the texture will be cleared to
1195 // 0's This means the depth and stencil test will pass and the red square is drawn.
TEST_P(TextureZeroInitTest,RenderingLoadingDepthStencilStoreOpClear)1196 TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) {
1197 wgpu::TextureDescriptor srcDescriptor =
1198 CreateTextureDescriptor(1, 1,
1199 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1200 wgpu::TextureUsage::RenderAttachment,
1201 kColorFormat);
1202 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
1203
1204 wgpu::TextureDescriptor depthStencilDescriptor =
1205 CreateTextureDescriptor(1, 1,
1206 wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc |
1207 wgpu::TextureUsage::CopyDst,
1208 kDepthStencilFormat);
1209 wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor);
1210
1211 // Setup the renderPass for the first pass.
1212 // We want to fail the depth and stencil test here so that nothing gets drawn and we can
1213 // see that the subresource correctly gets set as unintialized in the second pass
1214 utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
1215 depthStencilTexture.CreateView());
1216 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
1217 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
1218 renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 1.0f;
1219 renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 1u;
1220 renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Discard;
1221 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Discard;
1222 {
1223 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1224 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
1225 pass.SetPipeline(CreatePipelineForTest());
1226 pass.Draw(6);
1227 pass.EndPass();
1228 wgpu::CommandBuffer commandBuffer = encoder.Finish();
1229 // Expect 0 lazy clears, depth stencil texture will clear using loadop
1230 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
1231
1232 // The depth stencil test should fail and not draw because the depth stencil texture is
1233 // cleared to 1's by using loadOp clear and set values from descriptor.
1234 std::vector<RGBA8> expectedBlack(kSize * kSize, {0, 0, 0, 0});
1235 EXPECT_TEXTURE_EQ(expectedBlack.data(), srcTexture, {0, 0}, {kSize, kSize});
1236
1237 // Expect texture subresource initialized to be false since storeop is clear, sets
1238 // subresource as uninitialized
1239 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0,
1240 1, 0, 1));
1241 }
1242
1243 // Now we put the depth stencil texture back into renderpass, it should be cleared by loadop
1244 // because storeOp clear sets the subresource as uninitialized
1245 {
1246 renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
1247 renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
1248 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1249 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
1250 pass.SetPipeline(CreatePipelineForTest());
1251 pass.Draw(6);
1252 pass.EndPass();
1253 wgpu::CommandBuffer commandBuffer = encoder.Finish();
1254 // Expect 0 lazy clears, depth stencil texture will clear using loadop
1255 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer));
1256
1257 // Now the depth stencil test should pass since depth stencil texture is cleared to 0's by
1258 // loadop load and uninitialized subresource, so we should have a red square
1259 std::vector<RGBA8> expectedRed(kSize * kSize, {255, 0, 0, 255});
1260 EXPECT_TEXTURE_EQ(expectedRed.data(), srcTexture, {0, 0}, {kSize, kSize});
1261
1262 // Expect texture subresource initialized to be false since storeop is clear, sets
1263 // subresource as uninitialized
1264 EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0,
1265 1, 0, 1));
1266 }
1267 }
1268
1269 // Test that if one mip of a texture is initialized and another is uninitialized, lazy clearing the
1270 // uninitialized mip does not clear the initialized mip.
TEST_P(TextureZeroInitTest,PreservesInitializedMip)1271 TEST_P(TextureZeroInitTest, PreservesInitializedMip) {
1272 wgpu::TextureDescriptor sampleTextureDescriptor =
1273 CreateTextureDescriptor(2, 1,
1274 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1275 wgpu::TextureUsage::TextureBinding,
1276 kColorFormat);
1277 wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
1278
1279 wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
1280 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat);
1281 wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
1282
1283 // Fill the sample texture's second mip with data
1284 uint32_t mipSize = kSize >> 1;
1285 std::vector<uint8_t> data(kFormatBlockByteSize * mipSize * mipSize, 2);
1286 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
1287 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
1288 wgpu::ImageCopyBuffer imageCopyBuffer =
1289 utils::CreateImageCopyBuffer(stagingBuffer, 0, mipSize * kFormatBlockByteSize);
1290 wgpu::ImageCopyTexture imageCopyTexture =
1291 utils::CreateImageCopyTexture(sampleTexture, 1, {0, 0, 0});
1292 wgpu::Extent3D copySize = {mipSize, mipSize, 1};
1293 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1294 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
1295 wgpu::CommandBuffer commands = encoder.Finish();
1296 // Expect 0 lazy clears because the texture subresource will be completely copied to
1297 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
1298
1299 // Create render pipeline
1300 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
1301 renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
1302 renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest();
1303 renderPipelineDescriptor.cTargets[0].format = kColorFormat;
1304 wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
1305
1306 // Create bindgroup
1307 wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
1308 {{0, sampleTexture.CreateView()}});
1309
1310 // Encode pass and submit
1311 encoder = device.CreateCommandEncoder();
1312 utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
1313 renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
1314 renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
1315 renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
1316 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
1317 pass.SetPipeline(renderPipeline);
1318 pass.SetBindGroup(0, bindGroup);
1319 pass.Draw(6);
1320 pass.EndPass();
1321 commands = encoder.Finish();
1322 // Expect 1 lazy clears, because not all mips of the sample texture are initialized by
1323 // copyBufferToTexture.
1324 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
1325
1326 // Expect the rendered texture to be cleared since we copied from the uninitialized first
1327 // mip.
1328 std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
1329 EXPECT_LAZY_CLEAR(
1330 1u, EXPECT_TEXTURE_EQ(expectedWithZeros.data(), renderTexture, {0, 0}, {kSize, kSize}, 0));
1331
1332 // Expect the first mip to have been lazy cleared to 0.
1333 EXPECT_LAZY_CLEAR(
1334 0u, EXPECT_TEXTURE_EQ(expectedWithZeros.data(), sampleTexture, {0, 0}, {kSize, kSize}, 0));
1335
1336 // Expect the second mip to still be filled with 2.
1337 std::vector<RGBA8> expectedWithTwos(mipSize * mipSize, {2, 2, 2, 2});
1338 EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_EQ(expectedWithTwos.data(), sampleTexture, {0, 0},
1339 {mipSize, mipSize}, 1));
1340
1341 // Expect the whole texture to be initialized
1342 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(sampleTexture.Get(), 0, 2, 0, 1));
1343 }
1344
1345 // Test that if one layer of a texture is initialized and another is uninitialized, lazy clearing
1346 // the uninitialized layer does not clear the initialized layer.
TEST_P(TextureZeroInitTest,PreservesInitializedArrayLayer)1347 TEST_P(TextureZeroInitTest, PreservesInitializedArrayLayer) {
1348 // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES.
1349 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
1350
1351 wgpu::TextureDescriptor sampleTextureDescriptor =
1352 CreateTextureDescriptor(1, 2,
1353 wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1354 wgpu::TextureUsage::TextureBinding,
1355 kColorFormat);
1356 wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
1357
1358 wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
1359 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment, kColorFormat);
1360 wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
1361
1362 // Fill the sample texture's second array layer with data
1363 std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 2);
1364 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
1365 device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
1366 wgpu::ImageCopyBuffer imageCopyBuffer =
1367 utils::CreateImageCopyBuffer(stagingBuffer, 0, kSize * kFormatBlockByteSize);
1368 wgpu::ImageCopyTexture imageCopyTexture =
1369 utils::CreateImageCopyTexture(sampleTexture, 0, {0, 0, 1});
1370 wgpu::Extent3D copySize = {kSize, kSize, 1};
1371 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1372 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Size);
1373 wgpu::CommandBuffer commands = encoder.Finish();
1374 // Expect 0 lazy clears because the texture subresource will be completely copied to
1375 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
1376
1377 // Create render pipeline
1378 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
1379 renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
1380 renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest();
1381 renderPipelineDescriptor.cTargets[0].format = kColorFormat;
1382 wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
1383
1384 // Only sample from the uninitialized first layer.
1385 wgpu::TextureViewDescriptor textureViewDescriptor;
1386 textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
1387 textureViewDescriptor.arrayLayerCount = 1;
1388
1389 // Create bindgroup
1390 wgpu::BindGroup bindGroup =
1391 utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
1392 {{0, sampleTexture.CreateView(&textureViewDescriptor)}});
1393
1394 // Encode pass and submit
1395 encoder = device.CreateCommandEncoder();
1396 utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
1397 renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
1398 renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
1399 renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Discard;
1400 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
1401 pass.SetPipeline(renderPipeline);
1402 pass.SetBindGroup(0, bindGroup);
1403 pass.Draw(6);
1404 pass.EndPass();
1405 commands = encoder.Finish();
1406 // Expect 1 lazy clears, because not all array layers of the sample texture are initialized by
1407 // copyBufferToTexture.
1408 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
1409
1410 // Expect the rendered texture to be cleared since we copied from the uninitialized first
1411 // array layer.
1412 std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
1413 EXPECT_LAZY_CLEAR(
1414 1u, EXPECT_TEXTURE_EQ(expectedWithZeros.data(), renderTexture, {0, 0, 0}, {kSize, kSize}));
1415
1416 // Expect the first array layer to have been lazy cleared to 0.
1417 EXPECT_LAZY_CLEAR(
1418 0u, EXPECT_TEXTURE_EQ(expectedWithZeros.data(), sampleTexture, {0, 0, 0}, {kSize, kSize}));
1419
1420 // Expect the second array layer to still be filled with 2.
1421 std::vector<RGBA8> expectedWithTwos(kSize * kSize, {2, 2, 2, 2});
1422 EXPECT_LAZY_CLEAR(
1423 0u, EXPECT_TEXTURE_EQ(expectedWithTwos.data(), sampleTexture, {0, 0, 1}, {kSize, kSize}));
1424
1425 // Expect the whole texture to be initialized
1426 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(sampleTexture.Get(), 0, 1, 0, 2));
1427 }
1428
1429 // This is a regression test for crbug.com/dawn/451 where the lazy texture
1430 // init path on D3D12 had a divide-by-zero exception in the copy split logic.
TEST_P(TextureZeroInitTest,CopyTextureToBufferNonRenderableUnaligned)1431 TEST_P(TextureZeroInitTest, CopyTextureToBufferNonRenderableUnaligned) {
1432 // TODO(crbug.com/dawn/667): Work around the fact that some platforms do not support reading
1433 // from Snorm textures.
1434 DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("disable_snorm_read"));
1435
1436 wgpu::TextureDescriptor descriptor;
1437 descriptor.size.width = kUnalignedSize;
1438 descriptor.size.height = kUnalignedSize;
1439 descriptor.size.depthOrArrayLayers = 1;
1440 descriptor.format = wgpu::TextureFormat::R8Snorm;
1441 descriptor.usage = wgpu::TextureUsage::CopySrc;
1442 wgpu::Texture texture = device.CreateTexture(&descriptor);
1443
1444 {
1445 uint32_t bytesPerRow = Align(kUnalignedSize, kTextureBytesPerRowAlignment);
1446
1447 // Create and initialize the destination buffer to ensure we only count the times of
1448 // texture lazy initialization in this test.
1449 const uint64_t bufferSize = kUnalignedSize * bytesPerRow;
1450 const std::vector<uint8_t> initialBufferData(bufferSize, 0u);
1451 wgpu::Buffer buffer = utils::CreateBufferFromData(device, initialBufferData.data(),
1452 bufferSize, wgpu::BufferUsage::CopyDst);
1453
1454 wgpu::ImageCopyTexture imageCopyTexture =
1455 utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
1456 wgpu::ImageCopyBuffer imageCopyBuffer =
1457 utils::CreateImageCopyBuffer(buffer, 0, bytesPerRow);
1458 wgpu::Extent3D copySize = {kUnalignedSize, kUnalignedSize, 1};
1459
1460 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1461 encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBuffer, ©Size);
1462
1463 wgpu::CommandBuffer commands = encoder.Finish();
1464 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
1465 }
1466
1467 // Expect texture subresource initialized to be true
1468 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
1469 }
1470
1471 // In this test WriteTexture fully overwrites a texture
TEST_P(TextureZeroInitTest,WriteWholeTexture)1472 TEST_P(TextureZeroInitTest, WriteWholeTexture) {
1473 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
1474 1, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat);
1475 wgpu::Texture texture = device.CreateTexture(&descriptor);
1476
1477 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
1478 wgpu::Extent3D copySize = {kSize, kSize, 1};
1479
1480 wgpu::TextureDataLayout textureDataLayout;
1481 textureDataLayout.offset = 0;
1482 textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize;
1483 textureDataLayout.rowsPerImage = kSize;
1484
1485 std::vector<RGBA8> data(
1486 utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage,
1487 copySize, kColorFormat) /
1488 sizeof(RGBA8),
1489 {100, 100, 100, 100});
1490
1491 // The write overwrites the whole texture so we don't need to do lazy initialization.
1492 EXPECT_LAZY_CLEAR(
1493 0u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8),
1494 &textureDataLayout, ©Size));
1495
1496 // Expect texture initialized to be true
1497 EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
1498
1499 EXPECT_TEXTURE_EQ(data.data(), texture, {0, 0}, {kSize, kSize});
1500 }
1501
1502 // Test WriteTexture to a subset of the texture, lazy init is necessary to clear the other
1503 // half.
TEST_P(TextureZeroInitTest,WriteTextureHalf)1504 TEST_P(TextureZeroInitTest, WriteTextureHalf) {
1505 wgpu::TextureDescriptor descriptor =
1506 CreateTextureDescriptor(4, 1,
1507 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
1508 wgpu::TextureUsage::CopySrc,
1509 kColorFormat);
1510 wgpu::Texture texture = device.CreateTexture(&descriptor);
1511
1512 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(texture, 0, {0, 0, 0});
1513 wgpu::Extent3D copySize = {kSize / 2, kSize, 1};
1514
1515 wgpu::TextureDataLayout textureDataLayout;
1516 textureDataLayout.offset = 0;
1517 textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize / 2;
1518 textureDataLayout.rowsPerImage = kSize;
1519
1520 std::vector<RGBA8> data(
1521 utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage,
1522 copySize, kColorFormat) /
1523 sizeof(RGBA8),
1524 {100, 100, 100, 100});
1525
1526 EXPECT_LAZY_CLEAR(
1527 1u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8),
1528 &textureDataLayout, ©Size));
1529
1530 // Expect texture initialized to be true
1531 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1));
1532
1533 std::vector<RGBA8> expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0});
1534 // first half filled with 100, by the data
1535 EXPECT_TEXTURE_EQ(data.data(), texture, {0, 0}, {kSize / 2, kSize});
1536 // second half should be cleared
1537 EXPECT_TEXTURE_EQ(expectedZeros.data(), texture, {kSize / 2, 0}, {kSize / 2, kSize});
1538 }
1539
1540 // In this test WriteTexture fully overwrites a range of subresources, so lazy initialization
1541 // is needed for neither the subresources involved in the write nor the other subresources.
TEST_P(TextureZeroInitTest,WriteWholeTextureArray)1542 TEST_P(TextureZeroInitTest, WriteWholeTextureArray) {
1543 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
1544 1, 6, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat);
1545 wgpu::Texture texture = device.CreateTexture(&descriptor);
1546
1547 constexpr uint32_t kBaseArrayLayer = 2u;
1548 constexpr uint32_t kCopyLayerCount = 3u;
1549
1550 wgpu::ImageCopyTexture imageCopyTexture =
1551 utils::CreateImageCopyTexture(texture, 0, {0, 0, kBaseArrayLayer});
1552 wgpu::Extent3D copySize = {kSize, kSize, kCopyLayerCount};
1553
1554 wgpu::TextureDataLayout textureDataLayout;
1555 textureDataLayout.offset = 0;
1556 textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize;
1557 textureDataLayout.rowsPerImage = kSize;
1558
1559 std::vector<RGBA8> data(
1560 utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage,
1561 copySize, kColorFormat) /
1562 sizeof(RGBA8),
1563 {100, 100, 100, 100});
1564
1565 // The write overwrites the whole subresources so we don't need to do lazy initialization on
1566 // them.
1567 EXPECT_LAZY_CLEAR(
1568 0u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8),
1569 &textureDataLayout, ©Size));
1570
1571 // Expect texture subresource initialized to be true
1572 EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, kBaseArrayLayer,
1573 kCopyLayerCount));
1574
1575 for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) {
1576 EXPECT_TEXTURE_EQ(data.data(), texture, {0, 0, layer}, {kSize, kSize});
1577 }
1578 }
1579
1580 // Test WriteTexture to a subset of the subresource, lazy init is necessary to clear the other
1581 // half.
TEST_P(TextureZeroInitTest,WriteTextureArrayHalf)1582 TEST_P(TextureZeroInitTest, WriteTextureArrayHalf) {
1583 wgpu::TextureDescriptor descriptor =
1584 CreateTextureDescriptor(4, 6,
1585 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
1586 wgpu::TextureUsage::CopySrc,
1587 kColorFormat);
1588 wgpu::Texture texture = device.CreateTexture(&descriptor);
1589
1590 constexpr uint32_t kBaseArrayLayer = 2u;
1591 constexpr uint32_t kCopyLayerCount = 3u;
1592
1593 wgpu::ImageCopyTexture imageCopyTexture =
1594 utils::CreateImageCopyTexture(texture, 0, {0, 0, kBaseArrayLayer});
1595 wgpu::Extent3D copySize = {kSize / 2, kSize, kCopyLayerCount};
1596
1597 wgpu::TextureDataLayout textureDataLayout;
1598 textureDataLayout.offset = 0;
1599 textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize / 2;
1600 textureDataLayout.rowsPerImage = kSize;
1601
1602 std::vector<RGBA8> data(
1603 utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage,
1604 copySize, kColorFormat) /
1605 sizeof(RGBA8),
1606 {100, 100, 100, 100});
1607
1608 EXPECT_LAZY_CLEAR(
1609 1u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8),
1610 &textureDataLayout, ©Size));
1611
1612 // Expect texture subresource initialized to be true
1613 EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1,
1614 kBaseArrayLayer, kCopyLayerCount));
1615
1616 std::vector<RGBA8> expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0});
1617 for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) {
1618 // first half filled with 100, by the data
1619 EXPECT_TEXTURE_EQ(data.data(), texture, {0, 0, layer}, {kSize / 2, kSize});
1620 // second half should be cleared
1621 EXPECT_TEXTURE_EQ(expectedZeros.data(), texture, {kSize / 2, 0, layer}, {kSize / 2, kSize});
1622 }
1623 }
1624
1625 // In this test WriteTexture fully overwrites a texture at mip level.
TEST_P(TextureZeroInitTest,WriteWholeTextureAtMipLevel)1626 TEST_P(TextureZeroInitTest, WriteWholeTextureAtMipLevel) {
1627 wgpu::TextureDescriptor descriptor = CreateTextureDescriptor(
1628 4, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat);
1629 wgpu::Texture texture = device.CreateTexture(&descriptor);
1630
1631 constexpr uint32_t kMipLevel = 2;
1632 constexpr uint32_t kMipSize = kSize >> kMipLevel;
1633
1634 wgpu::ImageCopyTexture imageCopyTexture =
1635 utils::CreateImageCopyTexture(texture, kMipLevel, {0, 0, 0});
1636 wgpu::Extent3D copySize = {kMipSize, kMipSize, 1};
1637
1638 wgpu::TextureDataLayout textureDataLayout;
1639 textureDataLayout.offset = 0;
1640 textureDataLayout.bytesPerRow = kMipSize * kFormatBlockByteSize;
1641 textureDataLayout.rowsPerImage = kMipSize;
1642
1643 std::vector<RGBA8> data(
1644 utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage,
1645 copySize, kColorFormat) /
1646 sizeof(RGBA8),
1647 {100, 100, 100, 100});
1648
1649 // The write overwrites the whole texture so we don't need to do lazy initialization.
1650 EXPECT_LAZY_CLEAR(
1651 0u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8),
1652 &textureDataLayout, ©Size));
1653
1654 // Expect texture initialized to be true
1655 EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), kMipLevel, 1, 0, 1));
1656
1657 EXPECT_TEXTURE_EQ(data.data(), texture, {0, 0}, {kMipSize, kMipSize}, kMipLevel);
1658 }
1659
1660 // Test WriteTexture to a subset of the texture at mip level, lazy init is necessary to clear the
1661 // other half.
TEST_P(TextureZeroInitTest,WriteTextureHalfAtMipLevel)1662 TEST_P(TextureZeroInitTest, WriteTextureHalfAtMipLevel) {
1663 wgpu::TextureDescriptor descriptor =
1664 CreateTextureDescriptor(4, 1,
1665 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::TextureBinding |
1666 wgpu::TextureUsage::CopySrc,
1667 kColorFormat);
1668 wgpu::Texture texture = device.CreateTexture(&descriptor);
1669
1670 constexpr uint32_t kMipLevel = 2;
1671 constexpr uint32_t kMipSize = kSize >> kMipLevel;
1672
1673 wgpu::ImageCopyTexture imageCopyTexture =
1674 utils::CreateImageCopyTexture(texture, kMipLevel, {0, 0, 0});
1675 wgpu::Extent3D copySize = {kMipSize / 2, kMipSize, 1};
1676
1677 wgpu::TextureDataLayout textureDataLayout;
1678 textureDataLayout.offset = 0;
1679 textureDataLayout.bytesPerRow = kMipSize * kFormatBlockByteSize / 2;
1680 textureDataLayout.rowsPerImage = kMipSize;
1681
1682 std::vector<RGBA8> data(
1683 utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage,
1684 copySize, kColorFormat) /
1685 sizeof(RGBA8),
1686 {100, 100, 100, 100});
1687
1688 EXPECT_LAZY_CLEAR(
1689 1u, queue.WriteTexture(&imageCopyTexture, data.data(), data.size() * sizeof(RGBA8),
1690 &textureDataLayout, ©Size));
1691
1692 // Expect texture initialized to be true
1693 EXPECT_EQ(true,
1694 dawn_native::IsTextureSubresourceInitialized(texture.Get(), kMipLevel, 1, 0, 1));
1695
1696 std::vector<RGBA8> expectedZeros((kMipSize / 2) * kMipSize, {0, 0, 0, 0});
1697 // first half filled with 100, by the data
1698 EXPECT_TEXTURE_EQ(data.data(), texture, {0, 0}, {kMipSize / 2, kMipSize}, kMipLevel);
1699 // second half should be cleared
1700 EXPECT_TEXTURE_EQ(expectedZeros.data(), texture, {kMipSize / 2, 0}, {kMipSize / 2, kMipSize},
1701 kMipLevel);
1702 }
1703
1704 DAWN_INSTANTIATE_TEST(TextureZeroInitTest,
1705 D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}),
1706 D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"},
1707 {"use_d3d12_render_pass"}),
1708 OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}),
1709 OpenGLESBackend({"nonzero_clear_resources_on_creation_for_testing"}),
1710 MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}),
1711 VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"}));
1712
1713 class CompressedTextureZeroInitTest : public TextureZeroInitTest {
1714 protected:
SetUp()1715 void SetUp() override {
1716 DawnTest::SetUp();
1717
1718 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
1719 DAWN_TEST_UNSUPPORTED_IF(!IsBCFormatSupported());
1720 }
1721
GetRequiredFeatures()1722 std::vector<const char*> GetRequiredFeatures() override {
1723 mIsBCFormatSupported = SupportsFeatures({"texture-compression-bc"});
1724 if (!mIsBCFormatSupported) {
1725 return {};
1726 }
1727
1728 return {"texture-compression-bc"};
1729 }
1730
IsBCFormatSupported() const1731 bool IsBCFormatSupported() const {
1732 return mIsBCFormatSupported;
1733 }
1734
1735 // Copy the compressed texture data into the destination texture.
InitializeDataInCompressedTextureAndExpectLazyClear(wgpu::Texture bcCompressedTexture,wgpu::TextureDescriptor textureDescriptor,wgpu::Extent3D copyExtent3D,uint32_t viewMipmapLevel,uint32_t baseArrayLayer,size_t lazyClearCount)1736 void InitializeDataInCompressedTextureAndExpectLazyClear(
1737 wgpu::Texture bcCompressedTexture,
1738 wgpu::TextureDescriptor textureDescriptor,
1739 wgpu::Extent3D copyExtent3D,
1740 uint32_t viewMipmapLevel,
1741 uint32_t baseArrayLayer,
1742 size_t lazyClearCount) {
1743 uint32_t copyWidthInBlock = copyExtent3D.width / kFormatBlockByteSize;
1744 uint32_t copyHeightInBlock = copyExtent3D.height / kFormatBlockByteSize;
1745 uint32_t copyBytesPerRow =
1746 Align(copyWidthInBlock * utils::GetTexelBlockSizeInBytes(textureDescriptor.format),
1747 kTextureBytesPerRowAlignment);
1748
1749 std::vector<uint8_t> data(
1750 utils::RequiredBytesInCopy(copyBytesPerRow, copyHeightInBlock, copyExtent3D,
1751 textureDescriptor.format),
1752 1);
1753
1754 // Copy texture data from a staging buffer to the destination texture.
1755 wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), data.size(),
1756 wgpu::BufferUsage::CopySrc);
1757 wgpu::ImageCopyBuffer imageCopyBuffer =
1758 utils::CreateImageCopyBuffer(stagingBuffer, 0, copyBytesPerRow, copyHeightInBlock);
1759
1760 wgpu::ImageCopyTexture imageCopyTexture = utils::CreateImageCopyTexture(
1761 bcCompressedTexture, viewMipmapLevel, {0, 0, baseArrayLayer});
1762
1763 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1764 encoder.CopyBufferToTexture(&imageCopyBuffer, &imageCopyTexture, ©Extent3D);
1765 wgpu::CommandBuffer copy = encoder.Finish();
1766 EXPECT_LAZY_CLEAR(lazyClearCount, queue.Submit(1, ©));
1767 }
1768
1769 // Run the tests that copies pre-prepared BC format data into a BC texture and verifies if we
1770 // can render correctly with the pixel values sampled from the BC texture.
1771 // Expect that the texture subresource is initialized
TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(wgpu::TextureDescriptor textureDescriptor,wgpu::Extent3D copyExtent3D,wgpu::Extent3D nonPaddedCopyExtent,uint32_t viewMipmapLevel,uint32_t baseArrayLayer,size_t lazyClearCount,bool halfCopyTest=false)1772 void TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(
1773 wgpu::TextureDescriptor textureDescriptor,
1774 wgpu::Extent3D copyExtent3D,
1775 wgpu::Extent3D nonPaddedCopyExtent,
1776 uint32_t viewMipmapLevel,
1777 uint32_t baseArrayLayer,
1778 size_t lazyClearCount,
1779 bool halfCopyTest = false) {
1780 wgpu::Texture bcTexture = device.CreateTexture(&textureDescriptor);
1781 InitializeDataInCompressedTextureAndExpectLazyClear(bcTexture, textureDescriptor,
1782 copyExtent3D, viewMipmapLevel,
1783 baseArrayLayer, lazyClearCount);
1784
1785 SampleCompressedTextureAndVerifyColor(bcTexture, textureDescriptor, copyExtent3D,
1786 nonPaddedCopyExtent, viewMipmapLevel, baseArrayLayer,
1787 halfCopyTest);
1788 }
1789
SampleCompressedTextureAndVerifyColor(wgpu::Texture bcTexture,wgpu::TextureDescriptor textureDescriptor,wgpu::Extent3D copyExtent3D,wgpu::Extent3D nonPaddedCopyExtent,uint32_t viewMipmapLevel,uint32_t baseArrayLayer,bool halfCopyTest=false)1790 void SampleCompressedTextureAndVerifyColor(wgpu::Texture bcTexture,
1791 wgpu::TextureDescriptor textureDescriptor,
1792 wgpu::Extent3D copyExtent3D,
1793 wgpu::Extent3D nonPaddedCopyExtent,
1794 uint32_t viewMipmapLevel,
1795 uint32_t baseArrayLayer,
1796 bool halfCopyTest = false) {
1797 // Sample the compressed texture and verify the texture colors in the render target
1798 utils::BasicRenderPass renderPass =
1799 utils::CreateBasicRenderPass(device, textureDescriptor.size.width >> viewMipmapLevel,
1800 textureDescriptor.size.height >> viewMipmapLevel);
1801
1802 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
1803 {
1804 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
1805 utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
1806 renderPipelineDescriptor.cTargets[0].format = kColorFormat;
1807 renderPipelineDescriptor.vertex.module = CreateBasicVertexShaderForTest();
1808 renderPipelineDescriptor.cFragment.module = CreateSampledTextureFragmentShaderForTest();
1809 wgpu::RenderPipeline renderPipeline =
1810 device.CreateRenderPipeline(&renderPipelineDescriptor);
1811 pass.SetPipeline(renderPipeline);
1812
1813 wgpu::TextureViewDescriptor textureViewDescriptor = CreateTextureViewDescriptor(
1814 viewMipmapLevel, baseArrayLayer, textureDescriptor.format);
1815 wgpu::BindGroup bindGroup =
1816 utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
1817 {{0, bcTexture.CreateView(&textureViewDescriptor)}});
1818 pass.SetBindGroup(0, bindGroup);
1819 pass.Draw(6);
1820 pass.EndPass();
1821 }
1822
1823 wgpu::CommandBuffer commands = encoder.Finish();
1824 queue.Submit(1, &commands);
1825
1826 std::vector<RGBA8> expected(nonPaddedCopyExtent.width * nonPaddedCopyExtent.height,
1827 {0x00, 0x20, 0x08, 0xFF});
1828 EXPECT_TEXTURE_EQ(expected.data(), renderPass.color, {0, 0},
1829 {nonPaddedCopyExtent.width, nonPaddedCopyExtent.height});
1830 EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(bcTexture.Get(), viewMipmapLevel,
1831 1, baseArrayLayer, 1));
1832
1833 // If we only copied to half the texture, check the other half is initialized to black
1834 if (halfCopyTest) {
1835 std::vector<RGBA8> expectBlack(nonPaddedCopyExtent.width * nonPaddedCopyExtent.height,
1836 {0x00, 0x00, 0x00, 0xFF});
1837 EXPECT_TEXTURE_EQ(expectBlack.data(), renderPass.color, {copyExtent3D.width, 0},
1838 {nonPaddedCopyExtent.width, nonPaddedCopyExtent.height});
1839 }
1840 }
1841
1842 bool mIsBCFormatSupported = false;
1843 };
1844
1845 // Test that the clearing is skipped when we use a full mip copy (with the physical size different
1846 // than the virtual mip size)
TEST_P(CompressedTextureZeroInitTest,FullMipCopy)1847 TEST_P(CompressedTextureZeroInitTest, FullMipCopy) {
1848 wgpu::TextureDescriptor textureDescriptor;
1849 textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1850 wgpu::TextureUsage::TextureBinding;
1851 textureDescriptor.size = {60, 60, 1};
1852 textureDescriptor.mipLevelCount = 1;
1853 textureDescriptor.format = utils::kBCFormats[0];
1854
1855 TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(
1856 textureDescriptor, textureDescriptor.size, textureDescriptor.size, 0, 0, 0u);
1857 }
1858
1859 // Test that 1 lazy clear count happens when we copy to half the texture
TEST_P(CompressedTextureZeroInitTest,HalfCopyBufferToTexture)1860 TEST_P(CompressedTextureZeroInitTest, HalfCopyBufferToTexture) {
1861 // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL.
1862 DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
1863
1864 wgpu::TextureDescriptor textureDescriptor;
1865 textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1866 wgpu::TextureUsage::TextureBinding;
1867 constexpr static uint32_t kSize = 16;
1868 textureDescriptor.size = {kSize, kSize, 1};
1869 textureDescriptor.mipLevelCount = 1;
1870 textureDescriptor.format = utils::kBCFormats[0];
1871
1872 wgpu::Extent3D copyExtent3D = {kSize / 2, kSize, 1};
1873
1874 TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(
1875 textureDescriptor, copyExtent3D, copyExtent3D, 0, 0, 1u, true);
1876 }
1877
1878 // Test that 0 lazy clear count happens when we copy buffer to texture to a nonzero mip level
1879 // (with physical size different from the virtual mip size)
TEST_P(CompressedTextureZeroInitTest,FullCopyToNonZeroMipLevel)1880 TEST_P(CompressedTextureZeroInitTest, FullCopyToNonZeroMipLevel) {
1881 // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES.
1882 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
1883
1884 wgpu::TextureDescriptor textureDescriptor;
1885 textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1886 wgpu::TextureUsage::TextureBinding;
1887 constexpr static uint32_t kSize = 60;
1888 textureDescriptor.size = {kSize, kSize, 1};
1889 textureDescriptor.mipLevelCount = 3;
1890 textureDescriptor.format = utils::kBCFormats[0];
1891 const uint32_t kViewMipLevel = 2;
1892 const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel;
1893
1894 const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize);
1895
1896 wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel, kCopySizeAtLevel, 1};
1897
1898 TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(
1899 textureDescriptor, copyExtent3D, {kActualSizeAtLevel, kActualSizeAtLevel, 1}, kViewMipLevel,
1900 0, 0u);
1901 }
1902
1903 // Test that 1 lazy clear count happens when we copy buffer to half texture to a nonzero mip level
1904 // (with physical size different from the virtual mip size)
TEST_P(CompressedTextureZeroInitTest,HalfCopyToNonZeroMipLevel)1905 TEST_P(CompressedTextureZeroInitTest, HalfCopyToNonZeroMipLevel) {
1906 // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL.
1907 DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
1908
1909 wgpu::TextureDescriptor textureDescriptor;
1910 textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1911 wgpu::TextureUsage::TextureBinding;
1912 constexpr static uint32_t kSize = 60;
1913 textureDescriptor.size = {kSize, kSize, 1};
1914 textureDescriptor.mipLevelCount = 3;
1915 textureDescriptor.format = utils::kBCFormats[0];
1916 const uint32_t kViewMipLevel = 2;
1917 const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel;
1918
1919 const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize);
1920
1921 wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel / 2, kCopySizeAtLevel, 1};
1922
1923 TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(
1924 textureDescriptor, copyExtent3D, {kActualSizeAtLevel / 2, kActualSizeAtLevel, 1},
1925 kViewMipLevel, 0, 1u, true);
1926 }
1927
1928 // Test that 0 lazy clear count happens when we copy buffer to nonzero array layer
TEST_P(CompressedTextureZeroInitTest,FullCopyToNonZeroArrayLayer)1929 TEST_P(CompressedTextureZeroInitTest, FullCopyToNonZeroArrayLayer) {
1930 // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES.
1931 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
1932
1933 wgpu::TextureDescriptor textureDescriptor;
1934 textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1935 wgpu::TextureUsage::TextureBinding;
1936 constexpr static uint32_t kSize = 16;
1937 constexpr static uint32_t kArrayLayers = 4;
1938 textureDescriptor.size = {kSize, kSize, kArrayLayers};
1939 textureDescriptor.mipLevelCount = 1;
1940 textureDescriptor.format = utils::kBCFormats[0];
1941
1942 wgpu::Extent3D copyExtent3D = {kSize, kSize, 1};
1943
1944 TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(
1945 textureDescriptor, copyExtent3D, copyExtent3D, 0, kArrayLayers - 2, 0u);
1946 }
1947
1948 // Test that 1 lazy clear count happens when we copy buffer to half texture to a nonzero array layer
TEST_P(CompressedTextureZeroInitTest,HalfCopyToNonZeroArrayLayer)1949 TEST_P(CompressedTextureZeroInitTest, HalfCopyToNonZeroArrayLayer) {
1950 // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL.
1951 DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
1952
1953 wgpu::TextureDescriptor textureDescriptor;
1954 textureDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst |
1955 wgpu::TextureUsage::TextureBinding;
1956 constexpr static uint32_t kSize = 16;
1957 constexpr static uint32_t kArrayLayers = 4;
1958 textureDescriptor.size = {kSize, kSize, kArrayLayers};
1959 textureDescriptor.mipLevelCount = 3;
1960 textureDescriptor.format = utils::kBCFormats[0];
1961
1962 wgpu::Extent3D copyExtent3D = {kSize / 2, kSize, 1};
1963
1964 TestCopyRegionIntoBCFormatTexturesAndCheckSubresourceIsInitialized(
1965 textureDescriptor, copyExtent3D, copyExtent3D, 0, kArrayLayers - 2, 1u, true);
1966 }
1967
1968 // full copy texture to texture, 0 lazy clears are needed
TEST_P(CompressedTextureZeroInitTest,FullCopyTextureToTextureMipLevel)1969 TEST_P(CompressedTextureZeroInitTest, FullCopyTextureToTextureMipLevel) {
1970 // TODO(crbug.com/dawn/593): This test uses glTextureView() which is not supported on OpenGL ES.
1971 DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
1972
1973 // create srcTexture and fill it with data
1974 wgpu::TextureDescriptor srcDescriptor =
1975 CreateTextureDescriptor(3, 1,
1976 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc |
1977 wgpu::TextureUsage::CopyDst,
1978 utils::kBCFormats[0]);
1979 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
1980
1981 const uint32_t kViewMipLevel = 2;
1982 const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel;
1983
1984 const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize);
1985
1986 wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel, kCopySizeAtLevel, 1};
1987
1988 // fill srcTexture with data
1989 InitializeDataInCompressedTextureAndExpectLazyClear(srcTexture, srcDescriptor, copyExtent3D,
1990 kViewMipLevel, 0, 0u);
1991
1992 wgpu::ImageCopyTexture srcImageCopyTexture =
1993 utils::CreateImageCopyTexture(srcTexture, kViewMipLevel, {0, 0, 0});
1994
1995 // create dstTexture that we will copy to
1996 wgpu::TextureDescriptor dstDescriptor =
1997 CreateTextureDescriptor(3, 1,
1998 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc |
1999 wgpu::TextureUsage::TextureBinding,
2000 utils::kBCFormats[0]);
2001 wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor);
2002
2003 wgpu::ImageCopyTexture dstImageCopyTexture =
2004 utils::CreateImageCopyTexture(dstTexture, kViewMipLevel, {0, 0, 0});
2005
2006 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
2007 encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Extent3D);
2008 wgpu::CommandBuffer commands = encoder.Finish();
2009 // the dstTexture does not need to be lazy cleared since it's fully copied to
2010 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
2011
2012 SampleCompressedTextureAndVerifyColor(dstTexture, dstDescriptor, copyExtent3D,
2013 {kActualSizeAtLevel, kActualSizeAtLevel, 1},
2014 kViewMipLevel, 0);
2015 }
2016
2017 // half copy texture to texture, lazy clears are needed for noncopied half
TEST_P(CompressedTextureZeroInitTest,HalfCopyTextureToTextureMipLevel)2018 TEST_P(CompressedTextureZeroInitTest, HalfCopyTextureToTextureMipLevel) {
2019 // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL.
2020 DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
2021
2022 // create srcTexture with data
2023 wgpu::TextureDescriptor srcDescriptor =
2024 CreateTextureDescriptor(3, 1,
2025 wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopySrc |
2026 wgpu::TextureUsage::CopyDst,
2027 utils::kBCFormats[0]);
2028 wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
2029
2030 const uint32_t kViewMipLevel = 2;
2031 const uint32_t kActualSizeAtLevel = kSize >> kViewMipLevel;
2032
2033 const uint32_t kCopySizeAtLevel = Align(kActualSizeAtLevel, kFormatBlockByteSize);
2034
2035 wgpu::Extent3D copyExtent3D = {kCopySizeAtLevel / 2, kCopySizeAtLevel, 1};
2036
2037 // fill srcTexture with data
2038 InitializeDataInCompressedTextureAndExpectLazyClear(srcTexture, srcDescriptor, copyExtent3D,
2039 kViewMipLevel, 0, 1u);
2040
2041 wgpu::ImageCopyTexture srcImageCopyTexture =
2042 utils::CreateImageCopyTexture(srcTexture, kViewMipLevel, {0, 0, 0});
2043
2044 // create dstTexture that we will copy to
2045 wgpu::TextureDescriptor dstDescriptor =
2046 CreateTextureDescriptor(3, 1,
2047 wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc |
2048 wgpu::TextureUsage::TextureBinding,
2049 utils::kBCFormats[0]);
2050 wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor);
2051
2052 wgpu::ImageCopyTexture dstImageCopyTexture =
2053 utils::CreateImageCopyTexture(dstTexture, kViewMipLevel, {0, 0, 0});
2054
2055 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
2056 encoder.CopyTextureToTexture(&srcImageCopyTexture, &dstImageCopyTexture, ©Extent3D);
2057 wgpu::CommandBuffer commands = encoder.Finish();
2058 // expect 1 lazy clear count since the dstTexture needs to be lazy cleared when we only copy to
2059 // half texture
2060 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
2061
2062 SampleCompressedTextureAndVerifyColor(dstTexture, dstDescriptor, copyExtent3D,
2063 {kActualSizeAtLevel / 2, kActualSizeAtLevel, 1},
2064 kViewMipLevel, 0, true);
2065 }
2066
2067 // Test uploading then reading back from a 2D array compressed texture.
2068 // This is a regression test for a bug where the final destination buffer
2069 // was considered fully initialized even though there was a 256-byte
2070 // stride between images.
TEST_P(CompressedTextureZeroInitTest,Copy2DArrayCompressedB2T2B)2071 TEST_P(CompressedTextureZeroInitTest, Copy2DArrayCompressedB2T2B) {
2072 // TODO(crbug.com/dawn/643): diagnose and fix this failure on OpenGL.
2073 DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
2074
2075 // create srcTexture with data
2076 wgpu::TextureDescriptor textureDescriptor = CreateTextureDescriptor(
2077 4, 5, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, utils::kBCFormats[0]);
2078 textureDescriptor.size = {8, 8, 5};
2079 wgpu::Texture srcTexture = device.CreateTexture(&textureDescriptor);
2080
2081 uint32_t mipLevel = 2;
2082 wgpu::Extent3D copyExtent3D = {4, 4, 5};
2083
2084 uint32_t copyWidthInBlock = copyExtent3D.width / kFormatBlockByteSize;
2085 uint32_t copyHeightInBlock = copyExtent3D.height / kFormatBlockByteSize;
2086 uint32_t copyRowsPerImage = copyHeightInBlock;
2087 uint32_t copyBytesPerRow =
2088 Align(copyWidthInBlock * utils::GetTexelBlockSizeInBytes(textureDescriptor.format),
2089 kTextureBytesPerRowAlignment);
2090
2091 // Generate data to upload
2092 std::vector<uint8_t> data(utils::RequiredBytesInCopy(copyBytesPerRow, copyRowsPerImage,
2093 copyExtent3D, textureDescriptor.format));
2094 for (size_t i = 0; i < data.size(); ++i) {
2095 data[i] = i % 255;
2096 }
2097
2098 // Copy texture data from a staging buffer to the destination texture.
2099 wgpu::Buffer stagingBuffer =
2100 utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::CopySrc);
2101 wgpu::ImageCopyBuffer imageCopyBufferSrc =
2102 utils::CreateImageCopyBuffer(stagingBuffer, 0, copyBytesPerRow, copyRowsPerImage);
2103
2104 wgpu::ImageCopyTexture imageCopyTexture =
2105 utils::CreateImageCopyTexture(srcTexture, mipLevel, {0, 0, 0});
2106
2107 {
2108 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
2109 encoder.CopyBufferToTexture(&imageCopyBufferSrc, &imageCopyTexture, ©Extent3D);
2110 wgpu::CommandBuffer copy = encoder.Finish();
2111 EXPECT_LAZY_CLEAR(0u, queue.Submit(1, ©));
2112 }
2113
2114 // Create a buffer to read back the data. It is the same size as the upload buffer.
2115 wgpu::BufferDescriptor readbackDesc = {};
2116 readbackDesc.size = data.size();
2117 readbackDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
2118 wgpu::Buffer readbackBuffer = device.CreateBuffer(&readbackDesc);
2119
2120 // Copy the texture to the readback buffer.
2121 wgpu::ImageCopyBuffer imageCopyBufferDst =
2122 utils::CreateImageCopyBuffer(readbackBuffer, 0, copyBytesPerRow, copyRowsPerImage);
2123 {
2124 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
2125 encoder.CopyTextureToBuffer(&imageCopyTexture, &imageCopyBufferDst, ©Extent3D);
2126 wgpu::CommandBuffer copy = encoder.Finish();
2127
2128 // Expect a lazy clear because the padding in the copy is not touched.
2129 EXPECT_LAZY_CLEAR(1u, queue.Submit(1, ©));
2130 }
2131
2132 // Generate expected data. It is the same as the upload data, but padding is zero.
2133 std::vector<uint8_t> expected(data.size(), 0);
2134 for (uint32_t z = 0; z < copyExtent3D.depthOrArrayLayers; ++z) {
2135 for (uint32_t y = 0; y < copyHeightInBlock; ++y) {
2136 memcpy(&expected[copyBytesPerRow * y + copyBytesPerRow * copyRowsPerImage * z],
2137 &data[copyBytesPerRow * y + copyBytesPerRow * copyRowsPerImage * z],
2138 copyWidthInBlock * utils::GetTexelBlockSizeInBytes(textureDescriptor.format));
2139 }
2140 }
2141 // Check final contents
2142 EXPECT_BUFFER_U8_RANGE_EQ(expected.data(), readbackBuffer, 0, expected.size());
2143 }
2144
2145 DAWN_INSTANTIATE_TEST(CompressedTextureZeroInitTest,
2146 D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}),
2147 MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}),
2148 OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}),
2149 OpenGLESBackend({"nonzero_clear_resources_on_creation_for_testing"}),
2150 VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"}));
2151