Implement External Texture Binding Functionality

Adds functionality to BindGroupLayout and BindGroup to allow
GPUExternalTexture bindings.

Bug: dawn:728
Change-Id: I651b28606dceda15f0a944711ddba639df77c1a3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/47381
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Brandon Jones
2021-06-01 19:45:53 +00:00
committed by Dawn LUCI CQ
parent a9e39e11a8
commit 39633e2da2
32 changed files with 889 additions and 35 deletions

View File

@@ -1183,12 +1183,12 @@ TEST_P(BindGroupTests, ReallyLargeBindGroup) {
for (uint32_t i = 0; i < kMaxSampledTexturesPerShaderStage; ++i) {
wgpu::Texture texture = CreateTextureWithRedData(
wgpu::TextureFormat::R8Unorm, expectedValue, wgpu::TextureUsage::Sampled);
bgEntries.push_back({binding, nullptr, 0, 0, nullptr, texture.CreateView()});
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
interface << "[[group(0), binding(" << binding++ << ")]] "
<< "var tex" << i << " : texture_2d<f32>;\n";
bgEntries.push_back({binding, nullptr, 0, 0, device.CreateSampler(), nullptr});
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, device.CreateSampler(), nullptr});
interface << "[[group(0), binding(" << binding++ << ")]]"
<< "var samp" << i << " : sampler;\n";
@@ -1202,7 +1202,7 @@ TEST_P(BindGroupTests, ReallyLargeBindGroup) {
for (uint32_t i = 0; i < kMaxStorageTexturesPerShaderStage; ++i) {
wgpu::Texture texture = CreateTextureWithRedData(
wgpu::TextureFormat::R32Uint, expectedValue, wgpu::TextureUsage::Storage);
bgEntries.push_back({binding, nullptr, 0, 0, nullptr, texture.CreateView()});
bgEntries.push_back({nullptr, binding, nullptr, 0, 0, nullptr, texture.CreateView()});
interface << "[[group(0), binding(" << binding++ << ")]] "
<< "var image" << i << " : [[access(read)]] texture_storage_2d<r32uint>;\n";
@@ -1216,7 +1216,7 @@ TEST_P(BindGroupTests, ReallyLargeBindGroup) {
for (uint32_t i = 0; i < kMaxUniformBuffersPerShaderStage; ++i) {
wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
device, wgpu::BufferUsage::Uniform, {expectedValue, 0, 0, 0});
bgEntries.push_back({binding, buffer, 0, 4 * sizeof(uint32_t), nullptr, nullptr});
bgEntries.push_back({nullptr, binding, buffer, 0, 4 * sizeof(uint32_t), nullptr, nullptr});
interface << "[[block]] struct UniformBuffer" << i << R"({
value : u32;
@@ -1233,7 +1233,7 @@ TEST_P(BindGroupTests, ReallyLargeBindGroup) {
for (uint32_t i = 0; i < kMaxStorageBuffersPerShaderStage - 1; ++i) {
wgpu::Buffer buffer = utils::CreateBufferFromData<uint32_t>(
device, wgpu::BufferUsage::Storage, {expectedValue});
bgEntries.push_back({binding, buffer, 0, sizeof(uint32_t), nullptr, nullptr});
bgEntries.push_back({nullptr, binding, buffer, 0, sizeof(uint32_t), nullptr, nullptr});
interface << "[[block]] struct ReadOnlyStorageBuffer" << i << R"({
value : u32;
@@ -1250,7 +1250,7 @@ TEST_P(BindGroupTests, ReallyLargeBindGroup) {
wgpu::Buffer result = utils::CreateBufferFromData<uint32_t>(
device, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc, {0});
bgEntries.push_back({binding, result, 0, sizeof(uint32_t), nullptr, nullptr});
bgEntries.push_back({nullptr, binding, result, 0, sizeof(uint32_t), nullptr, nullptr});
interface << R"([[block]] struct ReadWriteStorageBuffer{
value : u32;

View File

@@ -13,6 +13,8 @@
// limitations under the License.
#include "tests/DawnTest.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/WGPUHelpers.h"
namespace {
@@ -43,8 +45,6 @@ namespace {
} // anonymous namespace
TEST_P(ExternalTextureTests, CreateExternalTextureSuccess) {
DAWN_TEST_UNSUPPORTED_IF(UsesWire());
wgpu::Texture texture = Create2DTexture(device, kWidth, kHeight, kFormat, kSampledUsage);
// Create a texture view for the external texture
@@ -61,6 +61,94 @@ TEST_P(ExternalTextureTests, CreateExternalTextureSuccess) {
ASSERT_NE(externalTexture.Get(), nullptr);
}
TEST_P(ExternalTextureTests, SampleExternalTexture) {
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
[[stage(vertex)]] fn main([[builtin(vertex_idx)]] VertexIndex : u32) -> [[builtin(position)]] vec4<f32> {
let positions : array<vec4<f32>, 3> = array<vec4<f32>, 3>(
vec4<f32>(-1.0, 1.0, 0.0, 1.0),
vec4<f32>(-1.0, -1.0, 0.0, 1.0),
vec4<f32>(1.0, 1.0, 0.0, 1.0)
);
return positions[VertexIndex];
})");
const wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var s : sampler;
[[group(0), binding(1)]] var t : texture_external;
[[stage(fragment)]] fn main([[builtin(position)]] FragCoord : vec4<f32>)
-> [[location(0)]] vec4<f32> {
return textureSampleLevel(t, s, FragCoord.xy / vec2<f32>(4.0, 4.0));
})");
wgpu::Texture sampledTexture =
Create2DTexture(device, kWidth, kHeight, kFormat,
wgpu::TextureUsage::Sampled | wgpu::TextureUsage::RenderAttachment);
wgpu::Texture renderTexture =
Create2DTexture(device, kWidth, kHeight, kFormat,
wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::RenderAttachment);
// Create a texture view for the external texture
wgpu::TextureView externalView = sampledTexture.CreateView();
// Initialize texture with green to ensure it is sampled from later.
{
utils::ComboRenderPassDescriptor renderPass({externalView}, nullptr);
renderPass.cColorAttachments[0].clearColor = {0.0f, 1.0f, 0.0f, 1.0f};
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// Create an ExternalTextureDescriptor from the texture view
wgpu::ExternalTextureDescriptor externalDesc;
externalDesc.plane0 = externalView;
externalDesc.format = kFormat;
// Import the external texture
wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc);
// Create a sampler and bind group
wgpu::Sampler sampler = device.CreateSampler();
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::SamplerBindingType::Filtering},
{1, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}});
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, bgl, {{0, sampler}, {1, externalTexture}});
// Pipeline Creation
utils::ComboRenderPipelineDescriptor descriptor;
descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
descriptor.vertex.module = vsModule;
descriptor.cFragment.module = fsModule;
descriptor.cTargets[0].format = kFormat;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
// Run the shader, which should sample from the external texture and draw a triangle into the
// upper left corner of the render texture.
wgpu::TextureView renderView = renderTexture.CreateView();
utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
{
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(3);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderTexture, 0, 0);
}
DAWN_INSTANTIATE_TEST(ExternalTextureTests,
D3D12Backend(),
MetalBackend(),

View File

@@ -53,9 +53,14 @@ class BindGroupValidationTest : public ValidationTest {
}
{ mSampler = device.CreateSampler(); }
{
mSampledTexture =
CreateTexture(wgpu::TextureUsage::Sampled, wgpu::TextureFormat::RGBA8Unorm, 1);
mSampledTexture = CreateTexture(wgpu::TextureUsage::Sampled, kDefaultTextureFormat, 1);
mSampledTextureView = mSampledTexture.CreateView();
wgpu::ExternalTextureDescriptor externalTextureDesc;
externalTextureDesc.format = kDefaultTextureFormat;
externalTextureDesc.plane0 = mSampledTextureView;
mExternalTexture = device.CreateExternalTexture(&externalTextureDesc);
mExternalTextureBindingEntry.externalTexture = mExternalTexture;
}
}
@@ -65,6 +70,12 @@ class BindGroupValidationTest : public ValidationTest {
wgpu::Sampler mSampler;
wgpu::Texture mSampledTexture;
wgpu::TextureView mSampledTextureView;
wgpu::ExternalTextureBindingEntry mExternalTextureBindingEntry;
static constexpr wgpu::TextureFormat kDefaultTextureFormat = wgpu::TextureFormat::RGBA8Unorm;
private:
wgpu::ExternalTexture mExternalTexture;
};
// Test the validation of BindGroupDescriptor::nextInChain
@@ -159,6 +170,11 @@ TEST_F(BindGroupValidationTest, SamplerBindingType) {
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.buffer = nullptr;
// Setting the external texture view as well is an error
binding.nextInChain = &mExternalTextureBindingEntry;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.nextInChain = nullptr;
// Setting the sampler to an error sampler is an error.
{
wgpu::SamplerDescriptor samplerDesc;
@@ -208,10 +224,15 @@ TEST_F(BindGroupValidationTest, TextureBindingType) {
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.buffer = nullptr;
// Setting the external texture view as well is an error
binding.nextInChain = &mExternalTextureBindingEntry;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.nextInChain = nullptr;
// Setting the texture view to an error texture view is an error.
{
wgpu::TextureViewDescriptor viewDesc;
viewDesc.format = wgpu::TextureFormat::RGBA8Unorm;
viewDesc.format = kDefaultTextureFormat;
viewDesc.dimension = wgpu::TextureViewDimension::e2D;
viewDesc.baseMipLevel = 0;
viewDesc.mipLevelCount = 0;
@@ -262,6 +283,11 @@ TEST_F(BindGroupValidationTest, BufferBindingType) {
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.sampler = nullptr;
// Setting the external texture view as well is an error
binding.nextInChain = &mExternalTextureBindingEntry;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.nextInChain = nullptr;
// Setting the buffer to an error buffer is an error.
{
wgpu::BufferDescriptor bufferDesc;
@@ -277,6 +303,91 @@ TEST_F(BindGroupValidationTest, BufferBindingType) {
}
}
// Check that an external texture binding must contain exactly an external texture
TEST_F(BindGroupValidationTest, ExternalTextureBindingType) {
// Create an external texture
wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::Sampled, kDefaultTextureFormat, 1);
wgpu::ExternalTextureDescriptor externalDesc;
externalDesc.plane0 = texture.CreateView();
externalDesc.format = kDefaultTextureFormat;
wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc);
// Create a bind group layout for a single external texture
wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}});
wgpu::BindGroupEntry binding;
binding.binding = 0;
binding.sampler = nullptr;
binding.textureView = nullptr;
binding.buffer = nullptr;
binding.offset = 0;
binding.size = 0;
wgpu::BindGroupDescriptor descriptor;
descriptor.layout = layout;
descriptor.entryCount = 1;
descriptor.entries = &binding;
// Not setting anything fails
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
// Control case: setting just the external texture works
wgpu::ExternalTextureBindingEntry externalBindingEntry;
externalBindingEntry.externalTexture = externalTexture;
binding.nextInChain = &externalBindingEntry;
device.CreateBindGroup(&descriptor);
// Setting the texture view as well is an error
binding.textureView = mSampledTextureView;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.textureView = nullptr;
// Setting the sampler as well is an error
binding.sampler = mSampler;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.sampler = nullptr;
// Setting the buffer as well is an error
binding.buffer = mUBO;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.buffer = nullptr;
// Setting the external texture to an error external texture is an error.
{
wgpu::ExternalTextureDescriptor errorExternalDesciptor;
errorExternalDesciptor.plane0 = texture.CreateView();
errorExternalDesciptor.format = wgpu::TextureFormat::R8Uint;
wgpu::ExternalTexture errorExternalTexture;
ASSERT_DEVICE_ERROR(errorExternalTexture =
device.CreateExternalTexture(&errorExternalDesciptor));
wgpu::ExternalTextureBindingEntry errorExternalBindingEntry;
errorExternalBindingEntry.externalTexture = errorExternalTexture;
binding.nextInChain = &errorExternalBindingEntry;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
binding.nextInChain = nullptr;
}
// Setting an external texture with another external texture chained is an error.
{
wgpu::ExternalTexture externalTexture2 = device.CreateExternalTexture(&externalDesc);
wgpu::ExternalTextureBindingEntry externalBindingEntry2;
externalBindingEntry2.externalTexture = externalTexture2;
externalBindingEntry.nextInChain = &externalBindingEntry2;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
}
// Chaining a struct that isn't an external texture binding entry is an error.
{
wgpu::ExternalTextureBindingLayout externalBindingLayout;
binding.nextInChain = &externalBindingLayout;
ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor));
}
}
// Check that a texture must have the correct usage
TEST_F(BindGroupValidationTest, TextureUsage) {
wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(
@@ -746,7 +857,7 @@ TEST_F(BindGroupLayoutValidationTest, PerStageLimits) {
wgpu::BindGroupLayoutEntry otherEntry;
};
std::array<TestInfo, 7> kTestInfos = {
std::array<TestInfo, 8> kTestInfos = {
TestInfo{kMaxSampledTexturesPerShaderStage, BGLEntryType(wgpu::TextureSampleType::Float),
BGLEntryType(wgpu::BufferBindingType::Uniform)},
TestInfo{kMaxSamplersPerShaderStage, BGLEntryType(wgpu::SamplerBindingType::Filtering),
@@ -765,7 +876,12 @@ TEST_F(BindGroupLayoutValidationTest, PerStageLimits) {
BGLEntryType(wgpu::BufferBindingType::Uniform)},
TestInfo{kMaxUniformBuffersPerShaderStage, BGLEntryType(wgpu::BufferBindingType::Uniform),
BGLEntryType(wgpu::TextureSampleType::Float)},
};
// External textures use multiple bindings (3 sampled textures, 1 sampler, 1 uniform buffer)
// that count towards the per stage binding limits. The number of external textures are
// currently restricted by the maximum number of sampled textures.
TestInfo{kMaxSampledTexturesPerShaderStage / kSampledTexturesPerExternalTexture,
BGLEntryType(&utils::kExternalTextureBindingLayout),
BGLEntryType(wgpu::BufferBindingType::Uniform)}};
for (TestInfo info : kTestInfos) {
wgpu::BindGroupLayout bgl[2];
@@ -829,6 +945,98 @@ TEST_F(BindGroupLayoutValidationTest, PerStageLimits) {
}
}
// External textures require multiple binding slots (3 sampled texture, 1 uniform buffer, 1
// sampler), so ensure that these count towards the limit when combined non-external texture
// bindings.
TEST_F(BindGroupLayoutValidationTest, PerStageLimitsWithExternalTexture) {
struct TestInfo {
uint32_t maxCount;
uint32_t bindingsPerExternalTexture;
wgpu::BindGroupLayoutEntry entry;
wgpu::BindGroupLayoutEntry otherEntry;
};
std::array<TestInfo, 3> kTestInfos = {
TestInfo{kMaxSampledTexturesPerShaderStage, kSampledTexturesPerExternalTexture,
BGLEntryType(wgpu::TextureSampleType::Float),
BGLEntryType(wgpu::BufferBindingType::Uniform)},
TestInfo{kMaxSamplersPerShaderStage, kSamplersPerExternalTexture,
BGLEntryType(wgpu::SamplerBindingType::Filtering),
BGLEntryType(wgpu::BufferBindingType::Uniform)},
TestInfo{kMaxUniformBuffersPerShaderStage, kUniformsPerExternalTexture,
BGLEntryType(wgpu::BufferBindingType::Uniform),
BGLEntryType(wgpu::TextureSampleType::Float)},
};
for (TestInfo info : kTestInfos) {
wgpu::BindGroupLayout bgl[2];
std::vector<utils::BindingLayoutEntryInitializationHelper> maxBindings;
// Create an external texture binding layout entry
wgpu::BindGroupLayoutEntry entry = BGLEntryType(&utils::kExternalTextureBindingLayout);
entry.binding = 0;
maxBindings.push_back(entry);
// Create the other bindings such that we reach the max bindings per stage when including
// the external texture.
for (uint32_t i = 1; i <= info.maxCount - info.bindingsPerExternalTexture; ++i) {
wgpu::BindGroupLayoutEntry entry = info.entry;
entry.binding = i;
maxBindings.push_back(entry);
}
// Ensure that creation without the external texture works.
bgl[0] = MakeBindGroupLayout(maxBindings.data(), maxBindings.size());
// Adding an extra binding of a different type works.
{
std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
wgpu::BindGroupLayoutEntry entry = info.otherEntry;
entry.binding = info.maxCount;
bindings.push_back(entry);
MakeBindGroupLayout(bindings.data(), bindings.size());
}
// Adding an extra binding of the maxed type in a different stage works
{
std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
wgpu::BindGroupLayoutEntry entry = info.entry;
entry.binding = info.maxCount;
entry.visibility = wgpu::ShaderStage::Fragment;
bindings.push_back(entry);
MakeBindGroupLayout(bindings.data(), bindings.size());
}
// Adding an extra binding of the maxed type and stage exceeds the per stage limit.
{
std::vector<utils::BindingLayoutEntryInitializationHelper> bindings = maxBindings;
wgpu::BindGroupLayoutEntry entry = info.entry;
entry.binding = info.maxCount;
bindings.push_back(entry);
ASSERT_DEVICE_ERROR(MakeBindGroupLayout(bindings.data(), bindings.size()));
}
// Creating a pipeline layout from the valid BGL works.
TestCreatePipelineLayout(bgl, 1, true);
// Adding an extra binding of a different type in a different BGL works
bgl[1] = utils::MakeBindGroupLayout(device, {info.otherEntry});
TestCreatePipelineLayout(bgl, 2, true);
{
// Adding an extra binding of the maxed type in a different stage works
wgpu::BindGroupLayoutEntry entry = info.entry;
entry.visibility = wgpu::ShaderStage::Fragment;
bgl[1] = utils::MakeBindGroupLayout(device, {entry});
TestCreatePipelineLayout(bgl, 2, true);
}
// Adding an extra binding of the maxed type in a different BGL exceeds the per stage limit.
bgl[1] = utils::MakeBindGroupLayout(device, {info.entry});
TestCreatePipelineLayout(bgl, 2, false);
}
}
// Check that dynamic buffer numbers exceed maximum value in one bind group layout.
TEST_F(BindGroupLayoutValidationTest, DynamicBufferNumberLimit) {
wgpu::BindGroupLayout bgl[2];
@@ -1847,6 +2055,28 @@ TEST_F(BindGroupLayoutCompatibilityTest, TextureViewDimension) {
wgpu::TextureViewDimension::e2D}})}));
}
// TODO(dawn:728) Enable this test when Dawn no longer relies on SPIRV-Cross to extract shader info.
TEST_F(BindGroupLayoutCompatibilityTest, DISABLED_ExternalTextureBindGroupLayoutCompatibility) {
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}});
// Test that an external texture binding works with a texture_external in the shader.
CreateFSRenderPipeline(R"(
[[group(0), binding(0)]] var myExternalTexture: texture_external;
[[stage(fragment)]] fn main() {
textureDimensions(myExternalTexture);
})",
{bgl});
// Test that an external texture binding doesn't work with a texture_2d<f32> in the shader.
ASSERT_DEVICE_ERROR(CreateFSRenderPipeline(R"(
[[group(0), binding(0)]] var myTexture: texture_2d<f32>;
[[stage(fragment)]] fn main() {
textureDimensions(myTexture);
})",
{bgl}));
}
class BindingsValidationTest : public BindGroupLayoutCompatibilityTest {
public:
void TestRenderPassBindings(const wgpu::BindGroup* bg,

View File

@@ -14,6 +14,9 @@
#include "tests/unittests/validation/ValidationTest.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/WGPUHelpers.h"
namespace {
class ExternalTextureTest : public ValidationTest {
public:
@@ -26,11 +29,41 @@ namespace {
descriptor.sampleCount = kDefaultSampleCount;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.format = kDefaultTextureFormat;
descriptor.usage = wgpu::TextureUsage::Sampled;
descriptor.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::RenderAttachment;
return descriptor;
}
protected:
void SetUp() override {
ValidationTest::SetUp();
queue = device.GetQueue();
}
wgpu::RenderPipeline CreateBasicRenderPipeline(wgpu::ExternalTexture externalTexture) {
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, &utils::kExternalTextureBindingLayout}});
bindGroup = utils::MakeBindGroup(device, bgl, {{0, externalTexture}});
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
return vec4<f32>();
})");
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
[[group(0), binding(0)]] var myExternalTexture: texture_external;
[[stage(fragment)]] fn main() {
textureDimensions(myExternalTexture);
})");
utils::ComboRenderPipelineDescriptor pipelineDescriptor;
pipelineDescriptor.vertex.module = vsModule;
pipelineDescriptor.cFragment.module = fsModule;
wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &bgl);
pipelineDescriptor.layout = pipelineLayout;
return device.CreateRenderPipeline(&pipelineDescriptor);
}
static constexpr uint32_t kWidth = 32;
static constexpr uint32_t kHeight = 32;
static constexpr uint32_t kDefaultDepth = 1;
@@ -39,6 +72,10 @@ namespace {
static constexpr wgpu::TextureFormat kDefaultTextureFormat =
wgpu::TextureFormat::RGBA8Unorm;
wgpu::Queue queue;
wgpu::RenderPipeline renderPipeline;
wgpu::BindGroup bindGroup;
};
TEST_F(ExternalTextureTest, CreateExternalTextureValidation) {
@@ -112,4 +149,110 @@ namespace {
}
}
// Test that submitting a command encoder that contains a destroyed external texture results in
// an error.
TEST_F(ExternalTextureTest, SubmitDestroyedExternalTexture) {
wgpu::TextureDescriptor textureDescriptor = CreateDefaultTextureDescriptor();
wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
wgpu::ExternalTextureDescriptor externalDesc;
externalDesc.format = kDefaultTextureFormat;
externalDesc.plane0 = texture.CreateView();
wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc);
wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline(externalTexture);
// Create another texture to use as a color attachment.
wgpu::TextureDescriptor renderTextureDescriptor = CreateDefaultTextureDescriptor();
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
wgpu::TextureView renderView = renderTexture.CreateView();
utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr);
// Control case should succeed.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
{
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// Destroying the external texture should result in an error.
{
externalTexture.Destroy();
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
{
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
}
// Test that submitting a command encoder that contains a destroyed external texture plane
// results in an error.
TEST_F(ExternalTextureTest, SubmitDestroyedExternalTexturePlane) {
wgpu::TextureDescriptor textureDescriptor = CreateDefaultTextureDescriptor();
wgpu::Texture texture = device.CreateTexture(&textureDescriptor);
wgpu::ExternalTextureDescriptor externalDesc;
externalDesc.format = kDefaultTextureFormat;
externalDesc.plane0 = texture.CreateView();
wgpu::ExternalTexture externalTexture = device.CreateExternalTexture(&externalDesc);
wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline(externalTexture);
// Create another texture to use as a color attachment.
wgpu::TextureDescriptor renderTextureDescriptor = CreateDefaultTextureDescriptor();
wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor);
wgpu::TextureView renderView = renderTexture.CreateView();
utils::ComboRenderPassDescriptor renderPass({renderView}, nullptr);
// Control case should succeed.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
{
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// Destroying an external texture underlying plane should result in an error.
{
texture.Destroy();
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
{
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
}
} // namespace

View File

@@ -274,6 +274,33 @@ TEST_F(GetBindGroupLayoutTests, BindingType) {
}
}
// Test that an external texture binding type matches a shader using texture_external.
// TODO(dawn:728) Enable this test once Dawn no longer relies on SPIRV-Cross to extract shader info.
// Consider combining with the similar test above.
TEST_F(GetBindGroupLayoutTests, DISABLED_ExternalTextureBindingType) {
// This test works assuming Dawn Native's object deduplication.
// Getting the same pointer to equivalent bind group layouts is an implementation detail of Dawn
// Native.
DAWN_SKIP_TEST_IF(UsesWire());
wgpu::BindGroupLayoutEntry binding = {};
binding.binding = 0;
binding.visibility = wgpu::ShaderStage::Fragment;
wgpu::BindGroupLayoutDescriptor desc = {};
desc.entryCount = 1;
desc.entries = &binding;
binding.nextInChain = &utils::kExternalTextureBindingLayout;
wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"(
[[group(0), binding(0)]] var myExternalTexture: texture_external;
[[stage(fragment)]] fn main() {
textureDimensions(myExternalTexture);
})");
EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get());
}
// Test that texture view dimension matches the shader.
TEST_F(GetBindGroupLayoutTests, ViewDimension) {
// This test works assuming Dawn Native's object deduplication.

View File

@@ -40,6 +40,7 @@ TEST_F(WireOptionalTests, OptionalObjectValue) {
entry.sampler = nullptr;
entry.textureView = nullptr;
entry.buffer = nullptr;
entry.nextInChain = nullptr;
WGPUBindGroupDescriptor bgDesc = {};
bgDesc.layout = bgl;