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:
parent
0eff2a2b46
commit
046389926f
|
@ -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
|
||||||
|
|
|
@ -40,6 +40,7 @@ namespace dawn_native {
|
||||||
MetalDisableSamplerCompare,
|
MetalDisableSamplerCompare,
|
||||||
DisableBaseVertex,
|
DisableBaseVertex,
|
||||||
DisableBaseInstance,
|
DisableBaseInstance,
|
||||||
|
UseD3D12SmallShaderVisibleHeapForTesting,
|
||||||
|
|
||||||
EnumCount,
|
EnumCount,
|
||||||
InvalidEnum = EnumCount,
|
InvalidEnum = EnumCount,
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"}));
|
||||||
|
|
Loading…
Reference in New Issue