mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-18 17:35:30 +00:00
Metal: Implement lazy texture clears
Bug: dawn:145 Change-Id: I73d161002cb09498e41838a10e9ac1db996c955d Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/14781 Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
committed by
Commit Bot service account
parent
0ecc48ecb7
commit
0d6619848d
@@ -911,4 +911,5 @@ namespace detail {
|
||||
template class ExpectEq<uint8_t>;
|
||||
template class ExpectEq<uint32_t>;
|
||||
template class ExpectEq<RGBA8>;
|
||||
template class ExpectEq<float>;
|
||||
} // namespace detail
|
||||
|
||||
@@ -48,6 +48,15 @@
|
||||
sizeof(RGBA8), \
|
||||
new detail::ExpectEq<RGBA8>(expected, (width) * (height)))
|
||||
|
||||
#define EXPECT_PIXEL_FLOAT_EQ(expected, texture, x, y) \
|
||||
AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(float), \
|
||||
new detail::ExpectEq<float>(expected))
|
||||
|
||||
#define EXPECT_TEXTURE_FLOAT_EQ(expected, texture, x, y, width, height, level, slice) \
|
||||
AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \
|
||||
sizeof(float), \
|
||||
new detail::ExpectEq<float>(expected, (width) * (height)))
|
||||
|
||||
#define EXPECT_LAZY_CLEAR(N, statement) \
|
||||
if (UsesWire()) { \
|
||||
statement; \
|
||||
@@ -377,6 +386,7 @@ namespace detail {
|
||||
extern template class ExpectEq<uint8_t>;
|
||||
extern template class ExpectEq<uint32_t>;
|
||||
extern template class ExpectEq<RGBA8>;
|
||||
extern template class ExpectEq<float>;
|
||||
} // namespace detail
|
||||
|
||||
#endif // TESTS_DAWNTEST_H_
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "tests/DawnTest.h"
|
||||
|
||||
#include "common/Constants.h"
|
||||
#include "utils/ComboRenderPipelineDescriptor.h"
|
||||
#include "utils/WGPUHelpers.h"
|
||||
|
||||
@@ -26,8 +27,8 @@ class NonzeroTextureCreationTests : public DawnTest {
|
||||
constexpr static uint32_t kSize = 128;
|
||||
};
|
||||
|
||||
// Test that texture clears to 1's because toggle is enabled.
|
||||
TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
|
||||
// Test that texture clears 0xFF because toggle is enabled.
|
||||
TEST_P(NonzeroTextureCreationTests, TextureCreationClears) {
|
||||
wgpu::TextureDescriptor descriptor;
|
||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
descriptor.size.width = kSize;
|
||||
@@ -40,11 +41,34 @@ TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) {
|
||||
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
RGBA8 filledWithOnes(255, 255, 255, 255);
|
||||
EXPECT_PIXEL_RGBA8_EQ(filledWithOnes, texture, 0, 0);
|
||||
RGBA8 filled(255, 255, 255, 255);
|
||||
EXPECT_PIXEL_RGBA8_EQ(filled, texture, 0, 0);
|
||||
}
|
||||
|
||||
// Test that non-zero mip level clears to 1's because toggle is enabled.
|
||||
// Test that a depth texture clears 0xFF because toggle is enabled.
|
||||
TEST_P(NonzeroTextureCreationTests, Depth32TextureCreationDepthClears) {
|
||||
// Copies from depth textures not supported on the OpenGL backend right now.
|
||||
DAWN_SKIP_TEST_IF(IsOpenGL());
|
||||
|
||||
wgpu::TextureDescriptor descriptor;
|
||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
descriptor.size.width = kSize;
|
||||
descriptor.size.height = kSize;
|
||||
descriptor.size.depth = 1;
|
||||
descriptor.arrayLayerCount = 1;
|
||||
descriptor.sampleCount = 1;
|
||||
descriptor.mipLevelCount = 1;
|
||||
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
|
||||
descriptor.format = wgpu::TextureFormat::Depth32Float;
|
||||
|
||||
// We can only really test Depth32Float here because Depth24Plus(Stencil8)? may be in an unknown
|
||||
// format.
|
||||
// TODO(crbug.com/dawn/145): Test other formats via sampling.
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
EXPECT_PIXEL_FLOAT_EQ(1.f, texture, 0, 0);
|
||||
}
|
||||
|
||||
// Test that non-zero mip level clears 0xFF because toggle is enabled.
|
||||
TEST_P(NonzeroTextureCreationTests, MipMapClears) {
|
||||
constexpr uint32_t mipLevels = 4;
|
||||
|
||||
@@ -61,15 +85,15 @@ TEST_P(NonzeroTextureCreationTests, MipMapClears) {
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
std::vector<RGBA8> expected;
|
||||
RGBA8 filledWithOnes(255, 255, 255, 255);
|
||||
RGBA8 filled(255, 255, 255, 255);
|
||||
for (uint32_t i = 0; i < kSize * kSize; ++i) {
|
||||
expected.push_back(filledWithOnes);
|
||||
expected.push_back(filled);
|
||||
}
|
||||
uint32_t mipSize = kSize >> 2;
|
||||
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, mipSize, mipSize, 2, 0);
|
||||
}
|
||||
|
||||
// Test that non-zero array layers clears to 1's because toggle is enabled.
|
||||
// Test that non-zero array layers clears 0xFF because toggle is enabled.
|
||||
TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
|
||||
constexpr uint32_t arrayLayers = 4;
|
||||
|
||||
@@ -86,15 +110,15 @@ TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) {
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
std::vector<RGBA8> expected;
|
||||
RGBA8 filledWithOnes(255, 255, 255, 255);
|
||||
RGBA8 filled(255, 255, 255, 255);
|
||||
for (uint32_t i = 0; i < kSize * kSize; ++i) {
|
||||
expected.push_back(filledWithOnes);
|
||||
expected.push_back(filled);
|
||||
}
|
||||
|
||||
EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 2);
|
||||
}
|
||||
|
||||
// Test that nonrenderable texture formats clear to 1's because toggle is enabled
|
||||
// Test that nonrenderable texture formats clear 0x01 because toggle is enabled
|
||||
TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
|
||||
wgpu::TextureDescriptor descriptor;
|
||||
descriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
@@ -123,11 +147,11 @@ TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) {
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
std::vector<uint32_t> expected(bufferSize, 1);
|
||||
std::vector<uint32_t> expected(bufferSize, 0x01010101);
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8);
|
||||
}
|
||||
|
||||
// Test that textures with more than 1 array layers and nonrenderable texture formats clear to 1's
|
||||
// Test that textures with more than 1 array layers and nonrenderable texture formats clear to 0x01
|
||||
// because toggle is enabled
|
||||
TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayers) {
|
||||
// TODO(natlee@microsoft.com): skip for now on opengl because TextureClear nonrenderable
|
||||
@@ -161,14 +185,129 @@ TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayer
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
queue.Submit(1, &commands);
|
||||
|
||||
std::vector<uint32_t> expectedWithZeros(bufferSize, 1);
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8);
|
||||
std::vector<uint32_t> expected(bufferSize, 0x01010101);
|
||||
EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8);
|
||||
}
|
||||
|
||||
// Test that all subresources of a renderable texture are filled because the toggle is enabled.
|
||||
TEST_P(NonzeroTextureCreationTests, AllSubresourcesFilled) {
|
||||
// TODO(crbug.com/dawn/145): Implement on other platforms.
|
||||
DAWN_SKIP_TEST_IF(!IsMetal());
|
||||
|
||||
wgpu::TextureDescriptor baseDescriptor;
|
||||
baseDescriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
baseDescriptor.size.width = kSize;
|
||||
baseDescriptor.size.height = kSize;
|
||||
baseDescriptor.size.depth = 1;
|
||||
baseDescriptor.sampleCount = 1;
|
||||
baseDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
baseDescriptor.mipLevelCount = 1;
|
||||
baseDescriptor.arrayLayerCount = 1;
|
||||
baseDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
|
||||
|
||||
RGBA8 filled(255, 255, 255, 255);
|
||||
|
||||
{
|
||||
wgpu::TextureDescriptor descriptor = baseDescriptor;
|
||||
// Some textures may be cleared with render pass load/store ops.
|
||||
// Test above the max attachment count.
|
||||
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
|
||||
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
wgpu::TextureDescriptor descriptor = baseDescriptor;
|
||||
descriptor.mipLevelCount = 3;
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) {
|
||||
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
wgpu::TextureDescriptor descriptor = baseDescriptor;
|
||||
// Some textures may be cleared with render pass load/store ops.
|
||||
// Test above the max attachment count.
|
||||
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
|
||||
descriptor.mipLevelCount = 3;
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
|
||||
for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) {
|
||||
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that all subresources of a nonrenderable texture are filled because the toggle is enabled.
|
||||
TEST_P(NonzeroTextureCreationTests, NonRenderableAllSubresourcesFilled) {
|
||||
// TODO(crbug.com/dawn/145): Implement on other platforms.
|
||||
DAWN_SKIP_TEST_IF(!IsMetal());
|
||||
|
||||
wgpu::TextureDescriptor baseDescriptor;
|
||||
baseDescriptor.dimension = wgpu::TextureDimension::e2D;
|
||||
baseDescriptor.size.width = kSize;
|
||||
baseDescriptor.size.height = kSize;
|
||||
baseDescriptor.size.depth = 1;
|
||||
baseDescriptor.sampleCount = 1;
|
||||
baseDescriptor.format = wgpu::TextureFormat::RGBA8Unorm;
|
||||
baseDescriptor.mipLevelCount = 1;
|
||||
baseDescriptor.arrayLayerCount = 1;
|
||||
baseDescriptor.usage = wgpu::TextureUsage::CopySrc;
|
||||
|
||||
RGBA8 filled(1, 1, 1, 1);
|
||||
|
||||
{
|
||||
wgpu::TextureDescriptor descriptor = baseDescriptor;
|
||||
// Some textures may be cleared with render pass load/store ops.
|
||||
// Test above the max attachment count.
|
||||
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
|
||||
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
wgpu::TextureDescriptor descriptor = baseDescriptor;
|
||||
descriptor.mipLevelCount = 3;
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) {
|
||||
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
wgpu::TextureDescriptor descriptor = baseDescriptor;
|
||||
// Some textures may be cleared with render pass load/store ops.
|
||||
// Test above the max attachment count.
|
||||
descriptor.arrayLayerCount = kMaxColorAttachments + 1;
|
||||
descriptor.mipLevelCount = 3;
|
||||
wgpu::Texture texture = device.CreateTexture(&descriptor);
|
||||
|
||||
for (uint32_t i = 0; i < descriptor.arrayLayerCount; ++i) {
|
||||
for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) {
|
||||
EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests,
|
||||
ForceToggles(D3D12Backend,
|
||||
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||
{"lazy_clear_resource_on_first_use"}),
|
||||
ForceToggles(MetalBackend,
|
||||
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||
{"lazy_clear_resource_on_first_use"}),
|
||||
ForceToggles(OpenGLBackend,
|
||||
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||
{"lazy_clear_resource_on_first_use"}),
|
||||
|
||||
@@ -127,7 +127,12 @@ TEST_P(TextureZeroInitTest, RenderingMipMapClearsToZero) {
|
||||
|
||||
utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
|
||||
|
||||
// Specify loadOp Load. Clear should be used to zero-initialize.
|
||||
renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
|
||||
// Specify non-zero clear color. It should still be cleared to zero.
|
||||
renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f};
|
||||
renderPass.renderPassInfo.cColorAttachments[0].attachment = view;
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
{
|
||||
// Texture's first usage is in BeginRenderPass's call to RecordRenderPass
|
||||
@@ -155,7 +160,12 @@ TEST_P(TextureZeroInitTest, RenderingArrayLayerClearsToZero) {
|
||||
|
||||
utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat);
|
||||
|
||||
// Specify loadOp Load. Clear should be used to zero-initialize.
|
||||
renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
|
||||
// Specify non-zero clear color. It should still be cleared to zero.
|
||||
renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f};
|
||||
renderPass.renderPassInfo.cColorAttachments[0].attachment = view;
|
||||
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
{
|
||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
|
||||
@@ -327,6 +337,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepth) {
|
||||
utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()},
|
||||
depthStencilTexture.CreateView());
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load;
|
||||
// Set clearDepth to non-zero. It should still be cleared to 0 by the loadOp.
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.5f;
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0;
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
|
||||
@@ -366,6 +378,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingStencil) {
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f;
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load;
|
||||
// Set clearStencil to non-zero. It should still be cleared to 0 by the loadOp.
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2;
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
|
||||
renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
|
||||
|
||||
@@ -760,6 +774,166 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test that if one mip of a texture is initialized and another is uninitialized, lazy clearing the
|
||||
// uninitialized mip does not clear the initialized mip.
|
||||
TEST_P(TextureZeroInitTest, PreservesInitializedMip) {
|
||||
// TODO(crbug.com/dawn/145): Fix this on other backends
|
||||
DAWN_SKIP_TEST_IF(!IsMetal());
|
||||
|
||||
wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor(
|
||||
2, 1,
|
||||
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled,
|
||||
kColorFormat);
|
||||
wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
|
||||
|
||||
wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
|
||||
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
|
||||
|
||||
wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
|
||||
1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat);
|
||||
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
|
||||
|
||||
// Fill the sample texture's second mip with data
|
||||
uint32_t mipSize = kSize >> 1;
|
||||
std::vector<uint8_t> data(kFormatBlockByteSize * mipSize * mipSize, 2);
|
||||
wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
|
||||
device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
|
||||
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
|
||||
wgpu::TextureCopyView textureCopyView =
|
||||
utils::CreateTextureCopyView(sampleTexture, 1, 0, {0, 0, 0});
|
||||
wgpu::Extent3D copySize = {mipSize, mipSize, 1};
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size);
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
// Expect 0 lazy clears because the texture subresource will be completely copied to
|
||||
EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
|
||||
|
||||
// Create render pipeline
|
||||
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
|
||||
renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
|
||||
renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
|
||||
renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
|
||||
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
|
||||
|
||||
// Create bindgroup
|
||||
wgpu::BindGroup bindGroup =
|
||||
utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
|
||||
{{0, sampler}, {1, sampleTexture.CreateView()}});
|
||||
|
||||
// Encode pass and submit
|
||||
encoder = device.CreateCommandEncoder();
|
||||
utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
|
||||
renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
|
||||
renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
|
||||
renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear;
|
||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||
pass.SetPipeline(renderPipeline);
|
||||
pass.SetBindGroup(0, bindGroup);
|
||||
pass.Draw(6, 1, 0, 0);
|
||||
pass.EndPass();
|
||||
commands = encoder.Finish();
|
||||
// Expect 1 lazy clears, because not all mips of the sample texture are initialized by
|
||||
// copyBufferToTexture.
|
||||
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
|
||||
|
||||
// Expect the rendered texture to be cleared since we copied from the uninitialized first
|
||||
// mip.
|
||||
std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
|
||||
EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0,
|
||||
kSize, kSize, 0, 0));
|
||||
|
||||
// Expect the first mip to have been lazy cleared to 0.
|
||||
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0,
|
||||
kSize, kSize, 0, 0));
|
||||
|
||||
// Expect the second mip to still be filled with 2.
|
||||
std::vector<RGBA8> expectedWithTwos(mipSize * mipSize, {2, 2, 2, 2});
|
||||
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0,
|
||||
mipSize, mipSize, 1, 0));
|
||||
}
|
||||
|
||||
// Test that if one layer of a texture is initialized and another is uninitialized, lazy clearing
|
||||
// the uninitialized layer does not clear the initialized layer.
|
||||
TEST_P(TextureZeroInitTest, PreservesInitializedArrayLayer) {
|
||||
// TODO(crbug.com/dawn/145): Fix this on other backends
|
||||
DAWN_SKIP_TEST_IF(!IsMetal());
|
||||
|
||||
wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor(
|
||||
1, 2,
|
||||
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled,
|
||||
kColorFormat);
|
||||
wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor);
|
||||
|
||||
wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor();
|
||||
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
|
||||
|
||||
wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor(
|
||||
1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat);
|
||||
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
|
||||
|
||||
// Fill the sample texture's second array layer with data
|
||||
std::vector<uint8_t> data(kFormatBlockByteSize * kSize * kSize, 2);
|
||||
wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(
|
||||
device, data.data(), static_cast<uint32_t>(data.size()), wgpu::BufferUsage::CopySrc);
|
||||
wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0);
|
||||
wgpu::TextureCopyView textureCopyView =
|
||||
utils::CreateTextureCopyView(sampleTexture, 0, 1, {0, 0, 0});
|
||||
wgpu::Extent3D copySize = {kSize, kSize, 1};
|
||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||
encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size);
|
||||
wgpu::CommandBuffer commands = encoder.Finish();
|
||||
// Expect 0 lazy clears because the texture subresource will be completely copied to
|
||||
EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands));
|
||||
|
||||
// Create render pipeline
|
||||
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
|
||||
renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest();
|
||||
renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest();
|
||||
renderPipelineDescriptor.cColorStates[0].format = kColorFormat;
|
||||
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor);
|
||||
|
||||
// Only sample from the uninitialized first layer.
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor;
|
||||
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
|
||||
textureViewDescriptor.arrayLayerCount = 1;
|
||||
|
||||
// Create bindgroup
|
||||
wgpu::BindGroup bindGroup =
|
||||
utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
|
||||
{{0, sampler}, {1, sampleTexture.CreateView(&textureViewDescriptor)}});
|
||||
|
||||
// Encode pass and submit
|
||||
encoder = device.CreateCommandEncoder();
|
||||
utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()});
|
||||
renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0};
|
||||
renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
|
||||
renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear;
|
||||
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||
pass.SetPipeline(renderPipeline);
|
||||
pass.SetBindGroup(0, bindGroup);
|
||||
pass.Draw(6, 1, 0, 0);
|
||||
pass.EndPass();
|
||||
commands = encoder.Finish();
|
||||
// Expect 1 lazy clears, because not all array layers of the sample texture are initialized by
|
||||
// copyBufferToTexture.
|
||||
EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands));
|
||||
|
||||
// Expect the rendered texture to be cleared since we copied from the uninitialized first
|
||||
// array layer.
|
||||
std::vector<RGBA8> expectedWithZeros(kSize * kSize, {0, 0, 0, 0});
|
||||
EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0,
|
||||
kSize, kSize, 0, 0));
|
||||
|
||||
// Expect the first array layer to have been lazy cleared to 0.
|
||||
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0,
|
||||
kSize, kSize, 0, 0));
|
||||
|
||||
// Expect the second array layer to still be filled with 2.
|
||||
std::vector<RGBA8> expectedWithTwos(kSize * kSize, {2, 2, 2, 2});
|
||||
EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0,
|
||||
kSize, kSize, 0, 1));
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(
|
||||
TextureZeroInitTest,
|
||||
ForceToggles(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
||||
@@ -767,4 +941,5 @@ DAWN_INSTANTIATE_TEST(
|
||||
{"nonzero_clear_resources_on_creation_for_testing"},
|
||||
{"use_d3d12_render_pass"}),
|
||||
ForceToggles(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
||||
ForceToggles(MetalBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
|
||||
ForceToggles(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));
|
||||
|
||||
Reference in New Issue
Block a user