D3D12: Add end2end tests for small shader-visible heaps.

Adds a toggle to force the use of small shader-visible heaps and
whitebox tests to verify bindgroup encoding correctness.

BUG=dawn:155

Change-Id: I4118b850d9f2cb445ae805aa68ebf4fab671261b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/16960
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Bryan Bernhart 2020-03-25 18:31:08 +00:00 committed by Commit Bot service account
parent 0eff2a2b46
commit 046389926f
5 changed files with 511 additions and 3 deletions

View File

@ -119,6 +119,10 @@ namespace dawn_native {
{"disable_base_instance", {"disable_base_instance",
"Disables the use of non-zero base instance which is unsupported on some " "Disables the use of non-zero base instance which is unsupported on some "
"platforms."}}, "platforms."}},
{Toggle::UseD3D12SmallShaderVisibleHeapForTesting,
{"use_d3d12_small_shader_visible_heap",
"Enable use of a small D3D12 shader visible heap, instead of using a large one by "
"default. This setting is used to test bindgroup encoding."}},
}}; }};
} // anonymous namespace } // anonymous namespace

View File

@ -40,6 +40,7 @@ namespace dawn_native {
MetalDisableSamplerCompare, MetalDisableSamplerCompare,
DisableBaseVertex, DisableBaseVertex,
DisableBaseInstance, DisableBaseInstance,
UseD3D12SmallShaderVisibleHeapForTesting,
EnumCount, EnumCount,
InvalidEnum = EnumCount, InvalidEnum = EnumCount,

View File

@ -410,6 +410,9 @@ namespace dawn_native { namespace d3d12 {
SetToggle(Toggle::UseD3D12ResourceHeapTier2, useResourceHeapTier2); SetToggle(Toggle::UseD3D12ResourceHeapTier2, useResourceHeapTier2);
SetToggle(Toggle::UseD3D12RenderPass, GetDeviceInfo().supportsRenderPass); SetToggle(Toggle::UseD3D12RenderPass, GetDeviceInfo().supportsRenderPass);
SetToggle(Toggle::UseD3D12ResidencyManagement, false); SetToggle(Toggle::UseD3D12ResidencyManagement, false);
// By default use the maximum shader-visible heap size allowed.
SetToggle(Toggle::UseD3D12SmallShaderVisibleHeapForTesting, false);
} }
MaybeError Device::WaitForIdleForDestruction() { MaybeError Device::WaitForIdleForDestruction() {

View File

@ -22,7 +22,14 @@ namespace dawn_native { namespace d3d12 {
static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV == 0, ""); static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV == 0, "");
static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER == 1, ""); static_assert(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER == 1, "");
uint32_t GetD3D12ShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE heapType) { // Thresholds should be adjusted (lower == faster) to avoid tests taking too long to complete.
static constexpr const uint32_t kShaderVisibleSmallHeapSizes[] = {1024, 512};
uint32_t GetD3D12ShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE heapType, bool useSmallSize) {
if (useSmallSize) {
return kShaderVisibleSmallHeapSizes[heapType];
}
switch (heapType) { switch (heapType) {
case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV: case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV:
return D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1; return D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1;
@ -144,7 +151,8 @@ namespace dawn_native { namespace d3d12 {
// TODO(bryan.bernhart@intel.com): Allocating to max heap size wastes memory // TODO(bryan.bernhart@intel.com): Allocating to max heap size wastes memory
// should the developer not allocate any bindings for the heap type. // should the developer not allocate any bindings for the heap type.
// Consider dynamically re-sizing GPU heaps. // Consider dynamically re-sizing GPU heaps.
const uint32_t descriptorCount = GetD3D12ShaderVisibleHeapSize(heapType); const uint32_t descriptorCount = GetD3D12ShaderVisibleHeapSize(
heapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
if (heap == nullptr) { if (heap == nullptr) {
D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor;

View File

@ -14,6 +14,7 @@
#include "tests/DawnTest.h" #include "tests/DawnTest.h"
#include "dawn_native/Toggles.h"
#include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" #include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h"
#include "utils/ComboRenderPipelineDescriptor.h" #include "utils/ComboRenderPipelineDescriptor.h"
@ -33,9 +34,63 @@ class D3D12DescriptorHeapTests : public DawnTest {
void TestSetUp() override { void TestSetUp() override {
DAWN_SKIP_TEST_IF(UsesWire()); DAWN_SKIP_TEST_IF(UsesWire());
mD3DDevice = reinterpret_cast<Device*>(device.Get()); mD3DDevice = reinterpret_cast<Device*>(device.Get());
mSimpleVSModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
#version 450
void main() {
const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f));
gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f);
})");
mSimpleFSModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout (location = 0) out vec4 fragColor;
layout (set = 0, binding = 0) uniform colorBuffer {
vec4 color;
};
void main() {
fragColor = color;
})");
}
utils::BasicRenderPass MakeRenderPass(const wgpu::Device& device,
uint32_t width,
uint32_t height,
wgpu::TextureFormat format) {
DAWN_ASSERT(width > 0 && height > 0);
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size.width = width;
descriptor.size.height = height;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = 1;
descriptor.sampleCount = 1;
descriptor.format = format;
descriptor.mipLevelCount = 1;
descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
wgpu::Texture color = device.CreateTexture(&descriptor);
return utils::BasicRenderPass(width, height, color);
}
uint32_t GetShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE heapType) const {
return mD3DDevice->GetShaderVisibleDescriptorAllocator()
->GetShaderVisibleHeapSizeForTesting(heapType);
}
std::array<float, 4> GetSolidColor(uint32_t n) const {
ASSERT(n >> 24 == 0);
float b = (n & 0xFF) / 255.0f;
float g = ((n >> 8) & 0xFF) / 255.0f;
float r = ((n >> 16) & 0xFF) / 255.0f;
return {r, g, b, 1};
} }
Device* mD3DDevice = nullptr; Device* mD3DDevice = nullptr;
wgpu::ShaderModule mSimpleVSModule;
wgpu::ShaderModule mSimpleFSModule;
}; };
// Verify the shader visible heaps switch over within a single submit. // Verify the shader visible heaps switch over within a single submit.
@ -198,4 +253,441 @@ TEST_P(D3D12DescriptorHeapTests, PoolHeapsInPendingAndMultipleSubmits) {
EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(heapType), kNumOfSwitches); EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(heapType), kNumOfSwitches);
} }
DAWN_INSTANTIATE_TEST(D3D12DescriptorHeapTests, D3D12Backend()); // Verify encoding multiple heaps worth of bindgroups.
// Shader-visible heaps will switch out |kNumOfHeaps| times.
TEST_P(D3D12DescriptorHeapTests, EncodeManyUBO) {
// This test draws a solid color triangle |heapSize| times. Each draw uses a new bindgroup that
// has its own UBO with a "color value" in the range [1... heapSize]. After |heapSize| draws,
// the result is the arithmetic sum of the sequence after the framebuffer is blended by
// accumulation. By checking for this sum, we ensure each bindgroup was encoded correctly.
DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled(
dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
utils::BasicRenderPass renderPass =
MakeRenderPass(device, kRTSize, kRTSize, wgpu::TextureFormat::R32Float);
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module = mSimpleVSModule;
pipelineDescriptor.cFragmentStage.module =
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout (location = 0) out float fragColor;
layout (set = 0, binding = 0) uniform buffer0 {
float heapSize;
};
void main() {
fragColor = heapSize;
})");
pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::R32Float;
pipelineDescriptor.cColorStates[0].colorBlend.operation = wgpu::BlendOperation::Add;
pipelineDescriptor.cColorStates[0].colorBlend.srcFactor = wgpu::BlendFactor::One;
pipelineDescriptor.cColorStates[0].colorBlend.dstFactor = wgpu::BlendFactor::One;
pipelineDescriptor.cColorStates[0].alphaBlend.operation = wgpu::BlendOperation::Add;
pipelineDescriptor.cColorStates[0].alphaBlend.srcFactor = wgpu::BlendFactor::One;
pipelineDescriptor.cColorStates[0].alphaBlend.dstFactor = wgpu::BlendFactor::One;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&pipelineDescriptor);
const uint32_t heapSize = GetShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
constexpr uint32_t kNumOfHeaps = 2;
const uint32_t numOfEncodedBindGroups = kNumOfHeaps * heapSize;
std::vector<wgpu::BindGroup> bindGroups;
for (uint32_t i = 0; i < numOfEncodedBindGroups; i++) {
const float color = i + 1;
wgpu::Buffer uniformBuffer =
utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform);
bindGroups.push_back(utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
{{0, uniformBuffer}}));
}
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(renderPipeline);
for (uint32_t i = 0; i < numOfEncodedBindGroups; ++i) {
pass.SetBindGroup(0, bindGroups[i]);
pass.Draw(3, 1, 0, 0);
}
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
float colorSum = numOfEncodedBindGroups * (numOfEncodedBindGroups + 1) / 2;
EXPECT_PIXEL_FLOAT_EQ(colorSum, renderPass.color, 0, 0);
}
// Verify encoding one bindgroup then a heaps worth in different submits.
// Shader-visible heaps should switch out once upon encoding 1 + |heapSize| descriptors.
// The first descriptor's memory will be reused when the second submit encodes |heapSize|
// descriptors.
TEST_P(D3D12DescriptorHeapTests, EncodeUBOOverflowMultipleSubmit) {
DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled(
dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module = mSimpleVSModule;
pipelineDescriptor.cFragmentStage.module = mSimpleFSModule;
pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat;
wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&pipelineDescriptor);
// Encode the first descriptor and submit.
{
std::array<float, 4> greenColor = {0, 1, 0, 1};
wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
device, &greenColor, sizeof(greenColor), wgpu::BufferUsage::Uniform);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(
device, renderPipeline.GetBindGroupLayout(0), {{0, uniformBuffer}});
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(renderPipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(3, 1, 0, 0);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0);
// Encode a heap worth of descriptors.
{
const uint32_t heapSize = GetShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
std::vector<wgpu::BindGroup> bindGroups;
for (uint32_t i = 0; i < heapSize - 1; i++) {
std::array<float, 4> fillColor = GetSolidColor(i + 1); // Avoid black
wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform);
bindGroups.push_back(utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
{{0, uniformBuffer}}));
}
std::array<float, 4> redColor = {1, 0, 0, 1};
wgpu::Buffer lastUniformBuffer = utils::CreateBufferFromData(
device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform);
bindGroups.push_back(utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0),
{{0, lastUniformBuffer, 0, sizeof(redColor)}}));
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(renderPipeline);
for (uint32_t i = 0; i < heapSize; ++i) {
pass.SetBindGroup(0, bindGroups[i]);
pass.Draw(3, 1, 0, 0);
}
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0);
}
// Verify encoding a heaps worth of bindgroups plus one more then reuse the first
// bindgroup in the same submit.
// Shader-visible heaps should switch out once then re-encode the first descriptor at a new offset
// in the heap.
TEST_P(D3D12DescriptorHeapTests, EncodeReuseUBOOverflow) {
DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled(
dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module = mSimpleVSModule;
pipelineDescriptor.cFragmentStage.module = mSimpleFSModule;
pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
std::array<float, 4> redColor = {1, 0, 0, 1};
wgpu::Buffer firstUniformBuffer = utils::CreateBufferFromData(
device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform);
std::vector<wgpu::BindGroup> bindGroups = {utils::MakeBindGroup(
device, pipeline.GetBindGroupLayout(0), {{0, firstUniformBuffer, 0, sizeof(redColor)}})};
const uint32_t heapSize = GetShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
for (uint32_t i = 0; i < heapSize; i++) {
const std::array<float, 4>& fillColor = GetSolidColor(i + 1); // Avoid black
wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform);
bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, uniformBuffer, 0, sizeof(fillColor)}}));
}
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
// Encode a heap worth of descriptors plus one more.
for (uint32_t i = 0; i < heapSize + 1; ++i) {
pass.SetBindGroup(0, bindGroups[i]);
pass.Draw(3, 1, 0, 0);
}
// Re-encode the first bindgroup again.
pass.SetBindGroup(0, bindGroups[0]);
pass.Draw(3, 1, 0, 0);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
// Make sure the first bindgroup was encoded correctly.
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0);
}
// Verify encoding a heaps worth of bindgroups plus one more in the first submit then reuse the
// first bindgroup again in the second submit.
// Shader-visible heaps should switch out once then re-encode the
// first descriptor at the same offset in the heap.
TEST_P(D3D12DescriptorHeapTests, EncodeReuseUBOMultipleSubmits) {
DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled(
dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting));
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module = mSimpleVSModule;
pipelineDescriptor.cFragmentStage.module = mSimpleFSModule;
pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
// Encode heap worth of descriptors plus one more.
std::array<float, 4> redColor = {1, 0, 0, 1};
wgpu::Buffer firstUniformBuffer = utils::CreateBufferFromData(
device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform);
std::vector<wgpu::BindGroup> bindGroups = {utils::MakeBindGroup(
device, pipeline.GetBindGroupLayout(0), {{0, firstUniformBuffer, 0, sizeof(redColor)}})};
const uint32_t heapSize = GetShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
for (uint32_t i = 0; i < heapSize; i++) {
std::array<float, 4> fillColor = GetSolidColor(i + 1); // Avoid black
wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform);
bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, uniformBuffer, 0, sizeof(fillColor)}}));
}
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
for (uint32_t i = 0; i < heapSize + 1; ++i) {
pass.SetBindGroup(0, bindGroups[i]);
pass.Draw(3, 1, 0, 0);
}
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// Re-encode the first bindgroup again.
{
std::array<float, 4> greenColor = {0, 1, 0, 1};
firstUniformBuffer.SetSubData(0, sizeof(greenColor), &greenColor);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroups[0]);
pass.Draw(3, 1, 0, 0);
pass.EndPass();
}
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// Make sure the first bindgroup was re-encoded correctly.
EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0);
}
// Verify encoding many sampler and ubo worth of bindgroups.
// Shader-visible heaps should switch out |kNumOfHeaps| times.
TEST_P(D3D12DescriptorHeapTests, EncodeManyUBOAndSamplers) {
// Create a solid filled texture.
wgpu::TextureDescriptor descriptor;
descriptor.dimension = wgpu::TextureDimension::e2D;
descriptor.size.width = kRTSize;
descriptor.size.height = kRTSize;
descriptor.size.depth = 1;
descriptor.arrayLayerCount = 1;
descriptor.sampleCount = 1;
descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
descriptor.mipLevelCount = 1;
descriptor.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment |
wgpu::TextureUsage::CopySrc;
wgpu::Texture texture = device.CreateTexture(&descriptor);
wgpu::TextureView textureView = texture.CreateView();
{
utils::BasicRenderPass renderPass = utils::BasicRenderPass(kRTSize, kRTSize, texture);
utils::ComboRenderPassDescriptor renderPassDesc({textureView});
renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
renderPassDesc.cColorAttachments[0].clearColor = {0.0f, 1.0f, 0.0f, 1.0f};
renderPass.renderPassInfo.cColorAttachments[0].attachment = textureView;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
auto pass = encoder.BeginRenderPass(&renderPassDesc);
pass.EndPass();
wgpu::CommandBuffer commandBuffer = encoder.Finish();
queue.Submit(1, &commandBuffer);
RGBA8 filled(0, 255, 0, 255);
EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 0, 0);
}
{
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module =
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
#version 450
layout (set = 0, binding = 0) uniform vertexUniformBuffer {
mat2 transform;
};
void main() {
const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f));
gl_Position = vec4(transform * pos[gl_VertexIndex], 0.f, 1.f);
})");
pipelineDescriptor.cFragmentStage.module =
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout (set = 0, binding = 1) uniform sampler sampler0;
layout (set = 0, binding = 2) uniform texture2D texture0;
layout (set = 0, binding = 3) uniform buffer0 {
vec4 color;
};
layout (location = 0) out vec4 fragColor;
void main() {
fragColor = texture(sampler2D(texture0, sampler0), gl_FragCoord.xy);
fragColor += color;
})");
utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat;
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor);
// Encode a heap worth of descriptors |kNumOfHeaps| times.
constexpr float dummy = 0.0f;
constexpr float transform[] = {1.f, 0.f, dummy, dummy, 0.f, 1.f, dummy, dummy};
wgpu::Buffer transformBuffer = utils::CreateBufferFromData(
device, &transform, sizeof(transform), wgpu::BufferUsage::Uniform);
wgpu::SamplerDescriptor samplerDescriptor;
wgpu::Sampler sampler = device.CreateSampler(&samplerDescriptor);
constexpr uint32_t kNumOfBindGroups = 4;
std::vector<wgpu::BindGroup> bindGroups;
for (uint32_t i = 0; i < kNumOfBindGroups - 1; i++) {
std::array<float, 4> fillColor = GetSolidColor(i + 1); // Avoid black
wgpu::Buffer uniformBuffer = utils::CreateBufferFromData(
device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform);
bindGroups.push_back(
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, transformBuffer, 0, sizeof(transformBuffer)},
{1, sampler},
{2, textureView},
{3, uniformBuffer, 0, sizeof(fillColor)}}));
}
std::array<float, 4> redColor = {1, 0, 0, 1};
wgpu::Buffer lastUniformBuffer = utils::CreateBufferFromData(
device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform);
bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{{0, transformBuffer, 0, sizeof(transform)},
{1, sampler},
{2, textureView},
{3, lastUniformBuffer, 0, sizeof(redColor)}}));
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
constexpr uint32_t kBindingsPerGroup = 4;
constexpr uint32_t kNumOfHeaps = 5;
const uint32_t heapSize = GetShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
const uint32_t bindGroupsPerHeap = heapSize / kBindingsPerGroup;
ASSERT_TRUE(heapSize % kBindingsPerGroup == 0);
for (uint32_t i = 0; i < kNumOfHeaps * bindGroupsPerHeap; ++i) {
pass.SetBindGroup(0, bindGroups[i % kNumOfBindGroups]);
pass.Draw(3, 1, 0, 0);
}
pass.EndPass();
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
// Final accumulated color is result of sampled + UBO color.
RGBA8 filled(255, 255, 0, 255);
RGBA8 notFilled(0, 0, 0, 0);
EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 0, 0);
EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, kRTSize - 1, 0);
}
}
DAWN_INSTANTIATE_TEST(D3D12DescriptorHeapTests,
D3D12Backend(),
D3D12Backend({"use_d3d12_small_shader_visible_heap"}));