blob: 7c24a69e3e9bef7d9de8684ff68d3bf7b6205c4c [file] [log] [blame]
// Copyright 2020 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "common/Assert.h"
#include "tests/DawnTest.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/WGPUHelpers.h"
namespace {
constexpr wgpu::CompareFunction kCompareFunctions[] = {
wgpu::CompareFunction::Never,
wgpu::CompareFunction::Less,
wgpu::CompareFunction::LessEqual,
wgpu::CompareFunction::Greater,
wgpu::CompareFunction::GreaterEqual,
wgpu::CompareFunction::Equal,
wgpu::CompareFunction::NotEqual,
wgpu::CompareFunction::Always,
};
// Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
constexpr float kCompareRefs[] = {-0.1, 0.4, 1.2};
// Test 0, below the ref, equal to, above the ref, and 1.
const std::vector<float> kNormalizedTextureValues = {0.0, 0.3, 0.4, 0.5, 1.0};
const std::vector<float> kNonNormalizedTextureValues = {-0.2, -0.1, 1.2, 1.3};
} // anonymous namespace
class DepthSamplingTest : public DawnTest {
protected:
void SetUp() override {
DawnTest::SetUp();
wgpu::BufferDescriptor uniformBufferDesc;
uniformBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
uniformBufferDesc.size = sizeof(float);
mUniformBuffer = device.CreateBuffer(&uniformBufferDesc);
wgpu::BufferDescriptor textureUploadDesc;
textureUploadDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
textureUploadDesc.size = sizeof(float);
mTextureUploadBuffer = device.CreateBuffer(&textureUploadDesc);
wgpu::TextureDescriptor inputTextureDesc;
inputTextureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled |
wgpu::TextureUsage::OutputAttachment;
inputTextureDesc.size = {1, 1, 1};
inputTextureDesc.format = wgpu::TextureFormat::Depth32Float;
mInputTexture = device.CreateTexture(&inputTextureDesc);
wgpu::TextureDescriptor outputTextureDesc;
outputTextureDesc.usage =
wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc;
outputTextureDesc.size = {1, 1, 1};
outputTextureDesc.format = wgpu::TextureFormat::R32Float;
mOutputTexture = device.CreateTexture(&outputTextureDesc);
wgpu::BufferDescriptor outputBufferDesc;
outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
outputBufferDesc.size = sizeof(float);
mOutputBuffer = device.CreateBuffer(&outputBufferDesc);
}
wgpu::RenderPipeline CreateSamplingRenderPipeline() {
wgpu::ShaderModule vsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
#version 450
void main() {
gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
gl_PointSize = 1.0;
}
)");
wgpu::ShaderModule fsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout(set = 0, binding = 0) uniform sampler samp;
layout(set = 0, binding = 1) uniform texture2D tex;
layout(location = 0) out float samplerResult;
void main() {
samplerResult = texture(sampler2D(tex, samp), vec2(0.5, 0.5)).r;
}
)");
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module = vsModule;
pipelineDescriptor.cFragmentStage.module = fsModule;
pipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::R32Float;
return device.CreateRenderPipeline(&pipelineDescriptor);
}
wgpu::ComputePipeline CreateSamplingComputePipeline() {
wgpu::ShaderModule csModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
#version 450
layout(set = 0, binding = 0) uniform sampler samp;
layout(set = 0, binding = 1) uniform texture2D tex;
layout(set = 0, binding = 2) writeonly buffer SamplerResult {
float samplerResult;
};
void main() {
samplerResult = texture(sampler2D(tex, samp), vec2(0.5, 0.5)).r;
}
)");
wgpu::ComputePipelineDescriptor pipelineDescriptor;
pipelineDescriptor.computeStage.module = csModule;
pipelineDescriptor.computeStage.entryPoint = "main";
return device.CreateComputePipeline(&pipelineDescriptor);
}
wgpu::RenderPipeline CreateComparisonRenderPipeline() {
wgpu::ShaderModule vsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(
#version 450
void main() {
gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
gl_PointSize = 1.0;
}
)");
wgpu::ShaderModule fsModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(
#version 450
layout(set = 0, binding = 0) uniform samplerShadow samp;
layout(set = 0, binding = 1) uniform texture2D tex;
layout(set = 0, binding = 2) uniform Uniforms {
float compareRef;
};
layout(location = 0) out float samplerResult;
void main() {
samplerResult = texture(sampler2DShadow(tex, samp), vec3(0.5, 0.5, compareRef));
}
)");
// TODO(dawn:367): Cannot use GetBindGroupLayout for comparison samplers without shader
// reflection data.
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ComparisonSampler},
{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture},
{2, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}});
utils::ComboRenderPipelineDescriptor pipelineDescriptor(device);
pipelineDescriptor.vertexStage.module = vsModule;
pipelineDescriptor.cFragmentStage.module = fsModule;
pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
pipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::R32Float;
return device.CreateRenderPipeline(&pipelineDescriptor);
}
wgpu::ComputePipeline CreateComparisonComputePipeline() {
wgpu::ShaderModule csModule =
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"(
#version 450
layout(set = 0, binding = 0) uniform samplerShadow samp;
layout(set = 0, binding = 1) uniform texture2D tex;
layout(set = 0, binding = 2) uniform Uniforms {
float compareRef;
};
layout(set = 0, binding = 3) writeonly buffer SamplerResult {
float samplerResult;
};
void main() {
samplerResult = texture(sampler2DShadow(tex, samp), vec3(0.5, 0.5, compareRef));
}
)");
// TODO(dawn:367): Cannot use GetBindGroupLayout without shader reflection data.
wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ComparisonSampler},
{1, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture},
{2, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer},
{3, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}});
wgpu::ComputePipelineDescriptor pipelineDescriptor;
pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl);
pipelineDescriptor.computeStage.module = csModule;
pipelineDescriptor.computeStage.entryPoint = "main";
return device.CreateComputePipeline(&pipelineDescriptor);
}
void UpdateInputTexture(wgpu::CommandEncoder commandEncoder, float textureValue) {
queue.WriteBuffer(mTextureUploadBuffer, 0, &textureValue, sizeof(float));
wgpu::BufferCopyView bufferCopyView = {};
bufferCopyView.buffer = mTextureUploadBuffer;
bufferCopyView.offset = 0;
bufferCopyView.bytesPerRow = kTextureBytesPerRowAlignment;
wgpu::TextureCopyView textureCopyView;
textureCopyView.texture = mInputTexture;
textureCopyView.origin = {0, 0, 0};
wgpu::Extent3D copySize = {1, 1, 1};
commandEncoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &copySize);
}
void DoSamplingTest(wgpu::RenderPipeline pipeline, std::vector<float> textureValues) {
wgpu::SamplerDescriptor samplerDesc;
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, mInputTexture.CreateView()},
});
for (float textureValue : textureValues) {
// Set the input depth texture to the provided texture value
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
UpdateInputTexture(commandEncoder, textureValue);
// Render into the output texture
{
utils::ComboRenderPassDescriptor passDescriptor({mOutputTexture.CreateView()});
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_FLOAT_EQ(textureValue, mOutputTexture, 0, 0);
}
}
void DoSamplingTest(wgpu::ComputePipeline pipeline, std::vector<float> textureValues) {
wgpu::SamplerDescriptor samplerDesc;
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, mInputTexture.CreateView()},
{2, mOutputBuffer}
});
for (float textureValue : textureValues) {
// Set the input depth texture to the provided texture value
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
UpdateInputTexture(commandEncoder, textureValue);
// Sample into the output buffer
{
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Dispatch(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(&textureValue), mOutputBuffer, 0);
}
}
static bool CompareFunctionPasses(float compareRef,
wgpu::CompareFunction compare,
float textureValue) {
switch (compare) {
case wgpu::CompareFunction::Never:
return false;
case wgpu::CompareFunction::Less:
return compareRef < textureValue;
case wgpu::CompareFunction::LessEqual:
return compareRef <= textureValue;
case wgpu::CompareFunction::Greater:
return compareRef > textureValue;
case wgpu::CompareFunction::GreaterEqual:
return compareRef >= textureValue;
case wgpu::CompareFunction::Equal:
return compareRef == textureValue;
case wgpu::CompareFunction::NotEqual:
return compareRef != textureValue;
case wgpu::CompareFunction::Always:
return true;
default:
return false;
}
}
void DoCompareRefTest(wgpu::RenderPipeline pipeline,
float compareRef,
wgpu::CompareFunction compare,
std::vector<float> textureValues) {
queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float));
wgpu::SamplerDescriptor samplerDesc;
samplerDesc.compare = compare;
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, mInputTexture.CreateView()},
{2, mUniformBuffer},
});
for (float textureValue : textureValues) {
// Set the input depth texture to the provided texture value
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
UpdateInputTexture(commandEncoder, textureValue);
// Render into the output texture
{
utils::ComboRenderPassDescriptor passDescriptor({mOutputTexture.CreateView()});
wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor);
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Draw(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_FLOAT_EQ(
CompareFunctionPasses(compareRef, compare, textureValue) ? 1.0 : 0.0,
mOutputTexture, 0, 0);
}
}
void DoCompareRefTest(wgpu::ComputePipeline pipeline,
float compareRef,
wgpu::CompareFunction compare,
std::vector<float> textureValues) {
queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float));
wgpu::SamplerDescriptor samplerDesc;
samplerDesc.compare = compare;
wgpu::Sampler sampler = device.CreateSampler(&samplerDesc);
wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0),
{
{0, sampler},
{1, mInputTexture.CreateView()},
{2, mUniformBuffer},
{3, mOutputBuffer}
});
for (float textureValue : textureValues) {
// Set the input depth texture to the provided texture value
wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder();
UpdateInputTexture(commandEncoder, textureValue);
// Sample into the output buffer
{
wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup);
pass.Dispatch(1);
pass.EndPass();
}
wgpu::CommandBuffer commands = commandEncoder.Finish();
queue.Submit(1, &commands);
float float0 = 0.f;
float float1 = 1.f;
float* expected =
CompareFunctionPasses(compareRef, compare, textureValue) ? &float1 : &float0;
EXPECT_BUFFER_U32_EQ(*reinterpret_cast<uint32_t*>(expected), mOutputBuffer, 0);
}
}
private:
wgpu::Buffer mUniformBuffer;
wgpu::Buffer mTextureUploadBuffer;
wgpu::Texture mInputTexture;
wgpu::Texture mOutputTexture;
wgpu::Buffer mOutputBuffer;
};
// Test that sampling a depth texture with a render pipeline works
TEST_P(DepthSamplingTest, SampleRender) {
// Test 0, between [0, 1], and 1.
DoSamplingTest(CreateSamplingRenderPipeline(), kNormalizedTextureValues);
}
// Test that sampling a depth texture with a compute pipeline works
TEST_P(DepthSamplingTest, SampleCompute) {
// Test 0, between [0, 1], and 1.
DoSamplingTest(CreateSamplingComputePipeline(), kNormalizedTextureValues);
}
// Test that sampling a depth texture with a render pipeline works,
// when the texture contents are outside the 0-1 range.
TEST_P(DepthSamplingTest, SampleNonNormalizedContentsRender) {
// TODO(enga): Sampling depth textures is clamped. Unless we reinterpret
// contents as R32F.
DAWN_SKIP_TEST_IF(IsOpenGL());
// Test values not between [0, 1]
DoSamplingTest(CreateSamplingRenderPipeline(), kNonNormalizedTextureValues);
}
// Test that sampling a depth texture with a render pipeline works,
// when the texture contents are outside the 0-1 range.
TEST_P(DepthSamplingTest, SampleNonNormalizedContentsCompute) {
// TODO(enga): Sampling depth textures is clamped. Unless we reinterpret
// contents as R32F.
DAWN_SKIP_TEST_IF(IsOpenGL());
// Test values not between [0, 1]
DoSamplingTest(CreateSamplingComputePipeline(), kNonNormalizedTextureValues);
}
// Test that sampling in a render pipeline with all of the compare functions works.
TEST_P(DepthSamplingTest, CompareFunctionsRender) {
wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline();
// Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
for (float compareRef : kCompareRefs) {
// Test 0, below the ref, equal to, above the ref, and 1.
for (wgpu::CompareFunction f : kCompareFunctions) {
DoCompareRefTest(pipeline, compareRef, f, kNormalizedTextureValues);
}
}
}
// Test that sampling in a render pipeline with all of the compare functions works.
TEST_P(DepthSamplingTest, CompareFunctionsCompute) {
// Comparison is always 0 on Mac Intel when using compute.
DAWN_SKIP_TEST_IF(IsMetal() && IsIntel());
wgpu::ComputePipeline pipeline = CreateComparisonComputePipeline();
// Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
for (float compareRef : kCompareRefs) {
// Test 0, below the ref, equal to, above the ref, and 1.
for (wgpu::CompareFunction f : kCompareFunctions) {
DoCompareRefTest(pipeline, compareRef, f, kNormalizedTextureValues);
}
}
}
// Test that sampling in a render pipeline with all of the compare functions works,
// when the texture contents are outside the 0-1 range.
TEST_P(DepthSamplingTest, CompareFunctionsNonNormalizedContentsRender) {
// TODO(enga): Sampling depth textures is clamped. Unless we reinterpret
// contents as R32F.
DAWN_SKIP_TEST_IF(IsOpenGL());
wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline();
// Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
for (float compareRef : kCompareRefs) {
// Test negative, and above 1.
for (wgpu::CompareFunction f : kCompareFunctions) {
DoCompareRefTest(pipeline, compareRef, f, kNonNormalizedTextureValues);
}
}
}
// Test that sampling in a compute pipeline with all of the compare functions works,
// when the texture contents are outside the 0-1 range.
TEST_P(DepthSamplingTest, CompareFunctionsNonNormalizedContentsCompute) {
// Comparison is always 0 on Mac Intel when using compute.
DAWN_SKIP_TEST_IF(IsMetal() && IsIntel());
// TODO(enga): Sampling depth textures is clamped. Unless we reinterpret
// contents as R32F.
DAWN_SKIP_TEST_IF(IsOpenGL());
wgpu::ComputePipeline pipeline = CreateComparisonComputePipeline();
// Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs.
for (float compareRef : kCompareRefs) {
// Test negative, and above 1.
for (wgpu::CompareFunction f : kCompareFunctions) {
DoCompareRefTest(pipeline, compareRef, f, kNormalizedTextureValues);
}
}
}
DAWN_INSTANTIATE_TEST(DepthSamplingTest,
D3D12Backend(),
MetalBackend(),
OpenGLBackend(),
VulkanBackend());