Vulkan: Clamp @builtin(frag_depth) with push constant values
Start using Tint's ClampFragDepth transform in the Vulkan backend when needed in order to correctly clamp @builtin(frag_depth) on Vulkan. Do this by always reserving 8 bytes of push constant space to contain the f32 min and max values from the last viewport command. Reenables relevant CTS tests that were suppressed on Vulkan. Bug: dawn:1125, dawn:1576, dawn:1616 Change-Id: I38f4f6c3c51c99b5e591a780fea9859537529534 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105642 Reviewed-by: Austin Eng <enga@chromium.org> Reviewed-by: Loko Kung <lokokung@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
452d6e3cc3
commit
8e68a89cfb
|
@ -26,6 +26,7 @@
|
||||||
#include "src/tint/inspector/inspector.h"
|
#include "src/tint/inspector/inspector.h"
|
||||||
#include "src/tint/reader/reader.h"
|
#include "src/tint/reader/reader.h"
|
||||||
#include "src/tint/transform/binding_remapper.h"
|
#include "src/tint/transform/binding_remapper.h"
|
||||||
|
#include "src/tint/transform/clamp_frag_depth.h"
|
||||||
#include "src/tint/transform/first_index_offset.h"
|
#include "src/tint/transform/first_index_offset.h"
|
||||||
#include "src/tint/transform/manager.h"
|
#include "src/tint/transform/manager.h"
|
||||||
#include "src/tint/transform/multiplanar_external_texture.h"
|
#include "src/tint/transform/multiplanar_external_texture.h"
|
||||||
|
|
|
@ -216,6 +216,10 @@ const PerStage<ProgrammableStage>& PipelineBase::GetAllStages() const {
|
||||||
return mStages;
|
return mStages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PipelineBase::HasStage(SingleShaderStage stage) const {
|
||||||
|
return mStageMask & StageBit(stage);
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::ShaderStage PipelineBase::GetStageMask() const {
|
wgpu::ShaderStage PipelineBase::GetStageMask() const {
|
||||||
return mStageMask;
|
return mStageMask;
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@ class PipelineBase : public ApiObjectBase, public CachedObject {
|
||||||
const RequiredBufferSizes& GetMinBufferSizes() const;
|
const RequiredBufferSizes& GetMinBufferSizes() const;
|
||||||
const ProgrammableStage& GetStage(SingleShaderStage stage) const;
|
const ProgrammableStage& GetStage(SingleShaderStage stage) const;
|
||||||
const PerStage<ProgrammableStage>& GetAllStages() const;
|
const PerStage<ProgrammableStage>& GetAllStages() const;
|
||||||
|
bool HasStage(SingleShaderStage stage) const;
|
||||||
wgpu::ShaderStage GetStageMask() const;
|
wgpu::ShaderStage GetStageMask() const;
|
||||||
|
|
||||||
ResultOrError<Ref<BindGroupLayoutBase>> GetBindGroupLayout(uint32_t groupIndex);
|
ResultOrError<Ref<BindGroupLayoutBase>> GetBindGroupLayout(uint32_t groupIndex);
|
||||||
|
|
|
@ -643,6 +643,10 @@ RenderPipelineBase::RenderPipelineBase(DeviceBase* device,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasStage(SingleShaderStage::Fragment)) {
|
||||||
|
mUsesFragDepth = GetStage(SingleShaderStage::Fragment).metadata->usesFragDepth;
|
||||||
|
}
|
||||||
|
|
||||||
SetContentHash(ComputeContentHash());
|
SetContentHash(ComputeContentHash());
|
||||||
GetObjectTrackingList()->Track(this);
|
GetObjectTrackingList()->Track(this);
|
||||||
|
|
||||||
|
@ -829,22 +833,24 @@ bool RenderPipelineBase::IsAlphaToCoverageEnabled() const {
|
||||||
|
|
||||||
const AttachmentState* RenderPipelineBase::GetAttachmentState() const {
|
const AttachmentState* RenderPipelineBase::GetAttachmentState() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
|
||||||
return mAttachmentState.Get();
|
return mAttachmentState.Get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RenderPipelineBase::WritesDepth() const {
|
bool RenderPipelineBase::WritesDepth() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
|
||||||
return mWritesDepth;
|
return mWritesDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RenderPipelineBase::WritesStencil() const {
|
bool RenderPipelineBase::WritesStencil() const {
|
||||||
ASSERT(!IsError());
|
ASSERT(!IsError());
|
||||||
|
|
||||||
return mWritesStencil;
|
return mWritesStencil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RenderPipelineBase::UsesFragDepth() const {
|
||||||
|
ASSERT(!IsError());
|
||||||
|
return mUsesFragDepth;
|
||||||
|
}
|
||||||
|
|
||||||
size_t RenderPipelineBase::ComputeContentHash() {
|
size_t RenderPipelineBase::ComputeContentHash() {
|
||||||
ObjectContentHasher recorder;
|
ObjectContentHasher recorder;
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,7 @@ class RenderPipelineBase : public PipelineBase {
|
||||||
bool IsAlphaToCoverageEnabled() const;
|
bool IsAlphaToCoverageEnabled() const;
|
||||||
bool WritesDepth() const;
|
bool WritesDepth() const;
|
||||||
bool WritesStencil() const;
|
bool WritesStencil() const;
|
||||||
|
bool UsesFragDepth() const;
|
||||||
|
|
||||||
const AttachmentState* GetAttachmentState() const;
|
const AttachmentState* GetAttachmentState() const;
|
||||||
|
|
||||||
|
@ -140,6 +141,7 @@ class RenderPipelineBase : public PipelineBase {
|
||||||
bool mUnclippedDepth = false;
|
bool mUnclippedDepth = false;
|
||||||
bool mWritesDepth = false;
|
bool mWritesDepth = false;
|
||||||
bool mWritesStencil = false;
|
bool mWritesStencil = false;
|
||||||
|
bool mUsesFragDepth = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dawn::native
|
} // namespace dawn::native
|
||||||
|
|
|
@ -646,6 +646,7 @@ ResultOrError<std::unique_ptr<EntryPointMetadata>> ReflectEntryPointUsingTint(
|
||||||
if (entryPoint.sample_index_used) {
|
if (entryPoint.sample_index_used) {
|
||||||
totalInterStageShaderComponents += 1;
|
totalInterStageShaderComponents += 1;
|
||||||
}
|
}
|
||||||
|
metadata->usesFragDepth = entryPoint.frag_depth_used;
|
||||||
|
|
||||||
metadata->totalInterStageShaderComponents = totalInterStageShaderComponents;
|
metadata->totalInterStageShaderComponents = totalInterStageShaderComponents;
|
||||||
DelayedInvalidIf(totalInterStageShaderComponents > maxInterStageShaderComponents,
|
DelayedInvalidIf(totalInterStageShaderComponents > maxInterStageShaderComponents,
|
||||||
|
|
|
@ -245,6 +245,7 @@ struct EntryPointMetadata {
|
||||||
std::unordered_set<std::string> initializedOverrides;
|
std::unordered_set<std::string> initializedOverrides;
|
||||||
|
|
||||||
bool usesNumWorkgroups = false;
|
bool usesNumWorkgroups = false;
|
||||||
|
bool usesFragDepth = false;
|
||||||
// Used at render pipeline validation.
|
// Used at render pipeline validation.
|
||||||
bool usesSampleMaskOutput = false;
|
bool usesSampleMaskOutput = false;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1490,9 +1490,13 @@ MaybeError CommandBuffer::EncodeRenderPass(id<MTLRenderCommandEncoder> encoder,
|
||||||
slopeScale:newPipeline->GetDepthBiasSlopeScale()
|
slopeScale:newPipeline->GetDepthBiasSlopeScale()
|
||||||
clamp:newPipeline->GetDepthBiasClamp()];
|
clamp:newPipeline->GetDepthBiasClamp()];
|
||||||
if (@available(macOS 10.11, iOS 11.0, *)) {
|
if (@available(macOS 10.11, iOS 11.0, *)) {
|
||||||
MTLDepthClipMode clipMode = newPipeline->HasUnclippedDepth()
|
// When using @builtin(frag_depth) we need to clamp to the viewport, otherwise
|
||||||
? MTLDepthClipModeClamp
|
// Metal writes the raw value to the depth buffer, which doesn't match other
|
||||||
: MTLDepthClipModeClip;
|
// APIs.
|
||||||
|
MTLDepthClipMode clipMode =
|
||||||
|
(newPipeline->UsesFragDepth() || newPipeline->HasUnclippedDepth())
|
||||||
|
? MTLDepthClipModeClamp
|
||||||
|
: MTLDepthClipModeClip;
|
||||||
[encoder setDepthClipMode:clipMode];
|
[encoder setDepthClipMode:clipMode];
|
||||||
}
|
}
|
||||||
newPipeline->Encode(encoder);
|
newPipeline->Encode(encoder);
|
||||||
|
|
|
@ -232,7 +232,9 @@ MaybeError Adapter::InitializeSupportedFeaturesImpl() {
|
||||||
mSupportedFeatures.EnableFeature(Feature::ChromiumExperimentalDp4a);
|
mSupportedFeatures.EnableFeature(Feature::ChromiumExperimentalDp4a);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDeviceInfo.HasExt(DeviceExt::DepthClipEnable) &&
|
// unclippedDepth=true translates to depthClipEnable=false, depthClamp=true
|
||||||
|
if (mDeviceInfo.features.depthClamp == VK_TRUE &&
|
||||||
|
mDeviceInfo.HasExt(DeviceExt::DepthClipEnable) &&
|
||||||
mDeviceInfo.depthClipEnableFeatures.depthClipEnable == VK_TRUE) {
|
mDeviceInfo.depthClipEnableFeatures.depthClipEnable == VK_TRUE) {
|
||||||
mSupportedFeatures.EnableFeature(Feature::DepthClipControl);
|
mSupportedFeatures.EnableFeature(Feature::DepthClipControl);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1108,6 +1108,23 @@ MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* recordingCon
|
||||||
DescriptorSetTracker descriptorSets = {};
|
DescriptorSetTracker descriptorSets = {};
|
||||||
RenderPipeline* lastPipeline = nullptr;
|
RenderPipeline* lastPipeline = nullptr;
|
||||||
|
|
||||||
|
// Tracking for the push constants needed by the ClampFragDepth transform.
|
||||||
|
// TODO(dawn:1125): Avoid the need for this when the depthClamp feature is available, but doing
|
||||||
|
// so would require fixing issue dawn:1576 first to have more dynamic push constant usage. (and
|
||||||
|
// also additional tests that the dirtying logic here is correct so with a Toggle we can test it
|
||||||
|
// on our infra).
|
||||||
|
ClampFragDepthArgs clampFragDepthArgs = {0.0f, 1.0f};
|
||||||
|
bool clampFragDepthArgsDirty = true;
|
||||||
|
auto ApplyClampFragDepthArgs = [&]() {
|
||||||
|
if (!clampFragDepthArgsDirty || lastPipeline == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
device->fn.CmdPushConstants(commands, ToBackend(lastPipeline->GetLayout())->GetHandle(),
|
||||||
|
VK_SHADER_STAGE_FRAGMENT_BIT, kClampFragDepthArgsOffset,
|
||||||
|
kClampFragDepthArgsSize, &clampFragDepthArgs);
|
||||||
|
clampFragDepthArgsDirty = false;
|
||||||
|
};
|
||||||
|
|
||||||
auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) {
|
auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Command::Draw: {
|
case Command::Draw: {
|
||||||
|
@ -1231,6 +1248,9 @@ MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* recordingCon
|
||||||
lastPipeline = pipeline;
|
lastPipeline = pipeline;
|
||||||
|
|
||||||
descriptorSets.OnSetPipeline(pipeline);
|
descriptorSets.OnSetPipeline(pipeline);
|
||||||
|
|
||||||
|
// Apply the deferred min/maxDepth push constants update if needed.
|
||||||
|
ApplyClampFragDepthArgs();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1302,6 +1322,12 @@ MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* recordingCon
|
||||||
}
|
}
|
||||||
|
|
||||||
device->fn.CmdSetViewport(commands, 0, 1, &viewport);
|
device->fn.CmdSetViewport(commands, 0, 1, &viewport);
|
||||||
|
|
||||||
|
// Try applying the push constants that contain min/maxDepth immediately. This can
|
||||||
|
// be deferred if no pipeline is currently bound.
|
||||||
|
clampFragDepthArgs = {viewport.minDepth, viewport.maxDepth};
|
||||||
|
clampFragDepthArgsDirty = true;
|
||||||
|
ApplyClampFragDepthArgs();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,8 @@ MaybeError ComputePipeline::Initialize() {
|
||||||
|
|
||||||
ShaderModule::ModuleAndSpirv moduleAndSpirv;
|
ShaderModule::ModuleAndSpirv moduleAndSpirv;
|
||||||
DAWN_TRY_ASSIGN(moduleAndSpirv,
|
DAWN_TRY_ASSIGN(moduleAndSpirv,
|
||||||
module->GetHandleAndSpirv(SingleShaderStage::Compute, computeStage, layout));
|
module->GetHandleAndSpirv(SingleShaderStage::Compute, computeStage, layout,
|
||||||
|
/*clampFragDepth*/ false));
|
||||||
|
|
||||||
createInfo.stage.module = moduleAndSpirv.module;
|
createInfo.stage.module = moduleAndSpirv.module;
|
||||||
createInfo.stage.pName = moduleAndSpirv.remappedEntryPoint;
|
createInfo.stage.pName = moduleAndSpirv.remappedEntryPoint;
|
||||||
|
|
|
@ -505,6 +505,7 @@ ResultOrError<VulkanDeviceKnobs> Device::CreateDevice(VkPhysicalDevice physicalD
|
||||||
ASSERT(deviceInfo.HasExt(DeviceExt::DepthClipEnable) &&
|
ASSERT(deviceInfo.HasExt(DeviceExt::DepthClipEnable) &&
|
||||||
deviceInfo.depthClipEnableFeatures.depthClipEnable == VK_TRUE);
|
deviceInfo.depthClipEnableFeatures.depthClipEnable == VK_TRUE);
|
||||||
|
|
||||||
|
usedKnobs.features.depthClamp = VK_TRUE;
|
||||||
usedKnobs.depthClipEnableFeatures.depthClipEnable = VK_TRUE;
|
usedKnobs.depthClipEnableFeatures.depthClipEnable = VK_TRUE;
|
||||||
featuresChain.Add(&usedKnobs.depthClipEnableFeatures,
|
featuresChain.Add(&usedKnobs.depthClipEnableFeatures,
|
||||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT);
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT);
|
||||||
|
|
|
@ -46,14 +46,20 @@ MaybeError PipelineLayout::Initialize() {
|
||||||
numSetLayouts++;
|
numSetLayouts++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always reserve push constant space for the ClampFragDepthArgs.
|
||||||
|
VkPushConstantRange depthClampArgsRange;
|
||||||
|
depthClampArgsRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
depthClampArgsRange.offset = kClampFragDepthArgsOffset;
|
||||||
|
depthClampArgsRange.size = kClampFragDepthArgsSize;
|
||||||
|
|
||||||
VkPipelineLayoutCreateInfo createInfo;
|
VkPipelineLayoutCreateInfo createInfo;
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||||
createInfo.pNext = nullptr;
|
createInfo.pNext = nullptr;
|
||||||
createInfo.flags = 0;
|
createInfo.flags = 0;
|
||||||
createInfo.setLayoutCount = numSetLayouts;
|
createInfo.setLayoutCount = numSetLayouts;
|
||||||
createInfo.pSetLayouts = AsVkArray(setLayouts.data());
|
createInfo.pSetLayouts = AsVkArray(setLayouts.data());
|
||||||
createInfo.pushConstantRangeCount = 0;
|
createInfo.pushConstantRangeCount = 1;
|
||||||
createInfo.pPushConstantRanges = nullptr;
|
createInfo.pPushConstantRanges = &depthClampArgsRange;
|
||||||
|
|
||||||
// Record cache key information now since the createInfo is not stored.
|
// Record cache key information now since the createInfo is not stored.
|
||||||
StreamIn(&mCacheKey, stream::Iterable(cachedObjects.data(), numSetLayouts), createInfo);
|
StreamIn(&mCacheKey, stream::Iterable(cachedObjects.data(), numSetLayouts), createInfo);
|
||||||
|
|
|
@ -24,6 +24,17 @@ namespace dawn::native::vulkan {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
|
|
||||||
|
// 8 bytes of push constant data are always reserved in the Vulkan pipeline layouts to be used by
|
||||||
|
// the code generated by the ClampFragDepth Tint transform. TODO(dawn:1576): Optimize usage of push
|
||||||
|
// constants so that they are only added to a pipeline / pipeline layout if needed.
|
||||||
|
struct ClampFragDepthArgs {
|
||||||
|
float min;
|
||||||
|
float max;
|
||||||
|
};
|
||||||
|
constexpr size_t kClampFragDepthArgsOffset = 0u;
|
||||||
|
constexpr size_t kClampFragDepthArgsSize = sizeof(ClampFragDepthArgs);
|
||||||
|
static_assert(kClampFragDepthArgsSize == 8u);
|
||||||
|
|
||||||
class PipelineLayout final : public PipelineLayoutBase {
|
class PipelineLayout final : public PipelineLayoutBase {
|
||||||
public:
|
public:
|
||||||
static ResultOrError<Ref<PipelineLayout>> Create(Device* device,
|
static ResultOrError<Ref<PipelineLayout>> Create(Device* device,
|
||||||
|
|
|
@ -343,45 +343,38 @@ MaybeError RenderPipeline::Initialize() {
|
||||||
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
|
||||||
uint32_t stageCount = 0;
|
uint32_t stageCount = 0;
|
||||||
|
|
||||||
for (auto stage : IterateStages(this->GetStageMask())) {
|
auto AddShaderStage = [&](SingleShaderStage stage, VkShaderStageFlagBits vkStage,
|
||||||
VkPipelineShaderStageCreateInfo shaderStage;
|
bool clampFragDepth) -> MaybeError {
|
||||||
|
|
||||||
const ProgrammableStage& programmableStage = GetStage(stage);
|
const ProgrammableStage& programmableStage = GetStage(stage);
|
||||||
ShaderModule* module = ToBackend(programmableStage.module.Get());
|
|
||||||
|
|
||||||
ShaderModule::ModuleAndSpirv moduleAndSpirv;
|
ShaderModule::ModuleAndSpirv moduleAndSpirv;
|
||||||
DAWN_TRY_ASSIGN(moduleAndSpirv,
|
DAWN_TRY_ASSIGN(moduleAndSpirv,
|
||||||
module->GetHandleAndSpirv(stage, programmableStage, layout));
|
ToBackend(programmableStage.module)
|
||||||
|
->GetHandleAndSpirv(stage, programmableStage, layout, clampFragDepth));
|
||||||
shaderStage.module = moduleAndSpirv.module;
|
|
||||||
shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
||||||
shaderStage.pNext = nullptr;
|
|
||||||
shaderStage.flags = 0;
|
|
||||||
shaderStage.pSpecializationInfo = nullptr;
|
|
||||||
shaderStage.pName = moduleAndSpirv.remappedEntryPoint;
|
|
||||||
|
|
||||||
switch (stage) {
|
|
||||||
case dawn::native::SingleShaderStage::Vertex: {
|
|
||||||
shaderStage.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case dawn::native::SingleShaderStage::Fragment: {
|
|
||||||
shaderStage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
// For render pipeline only Vertex and Fragment stage is possible
|
|
||||||
DAWN_UNREACHABLE();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DAWN_ASSERT(stageCount < 2);
|
|
||||||
shaderStages[stageCount] = shaderStage;
|
|
||||||
stageCount++;
|
|
||||||
|
|
||||||
// Record cache key for each shader since it will become inaccessible later on.
|
// Record cache key for each shader since it will become inaccessible later on.
|
||||||
StreamIn(&mCacheKey, stream::Iterable(moduleAndSpirv.spirv, moduleAndSpirv.wordCount));
|
StreamIn(&mCacheKey, stream::Iterable(moduleAndSpirv.spirv, moduleAndSpirv.wordCount));
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo* shaderStage = &shaderStages[stageCount];
|
||||||
|
shaderStage->module = moduleAndSpirv.module;
|
||||||
|
shaderStage->sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
shaderStage->pNext = nullptr;
|
||||||
|
shaderStage->flags = 0;
|
||||||
|
shaderStage->pSpecializationInfo = nullptr;
|
||||||
|
shaderStage->stage = vkStage;
|
||||||
|
shaderStage->pName = moduleAndSpirv.remappedEntryPoint;
|
||||||
|
|
||||||
|
stageCount++;
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add the vertex stage that's always present.
|
||||||
|
DAWN_TRY(AddShaderStage(SingleShaderStage::Vertex, VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
|
/*clampFragDepth*/ false));
|
||||||
|
|
||||||
|
// Add the fragment stage if present.
|
||||||
|
if (GetStageMask() & wgpu::ShaderStage::Fragment) {
|
||||||
|
bool clampFragDepth = UsesFragDepth() && !HasUnclippedDepth();
|
||||||
|
DAWN_TRY(AddShaderStage(SingleShaderStage::Fragment, VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
clampFragDepth));
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations;
|
PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations;
|
||||||
|
@ -422,7 +415,7 @@ MaybeError RenderPipeline::Initialize() {
|
||||||
rasterization.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
rasterization.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||||
rasterization.pNext = nullptr;
|
rasterization.pNext = nullptr;
|
||||||
rasterization.flags = 0;
|
rasterization.flags = 0;
|
||||||
rasterization.depthClampEnable = VK_FALSE;
|
rasterization.depthClampEnable = HasUnclippedDepth();
|
||||||
rasterization.rasterizerDiscardEnable = VK_FALSE;
|
rasterization.rasterizerDiscardEnable = VK_FALSE;
|
||||||
rasterization.polygonMode = VK_POLYGON_MODE_FILL;
|
rasterization.polygonMode = VK_POLYGON_MODE_FILL;
|
||||||
rasterization.cullMode = VulkanCullMode(GetCullMode());
|
rasterization.cullMode = VulkanCullMode(GetCullMode());
|
||||||
|
|
|
@ -175,6 +175,7 @@ ShaderModule::~ShaderModule() = default;
|
||||||
X(bool, disableWorkgroupInit) \
|
X(bool, disableWorkgroupInit) \
|
||||||
X(bool, disableSymbolRenaming) \
|
X(bool, disableSymbolRenaming) \
|
||||||
X(bool, useZeroInitializeWorkgroupMemoryExtension) \
|
X(bool, useZeroInitializeWorkgroupMemoryExtension) \
|
||||||
|
X(bool, clampFragDepth) \
|
||||||
X(CacheKey::UnsafeUnkeyedValue<dawn::platform::Platform*>, tracePlatform)
|
X(CacheKey::UnsafeUnkeyedValue<dawn::platform::Platform*>, tracePlatform)
|
||||||
|
|
||||||
DAWN_MAKE_CACHE_REQUEST(SpirvCompilationRequest, SPIRV_COMPILATION_REQUEST_MEMBERS);
|
DAWN_MAKE_CACHE_REQUEST(SpirvCompilationRequest, SPIRV_COMPILATION_REQUEST_MEMBERS);
|
||||||
|
@ -183,7 +184,8 @@ DAWN_MAKE_CACHE_REQUEST(SpirvCompilationRequest, SPIRV_COMPILATION_REQUEST_MEMBE
|
||||||
ResultOrError<ShaderModule::ModuleAndSpirv> ShaderModule::GetHandleAndSpirv(
|
ResultOrError<ShaderModule::ModuleAndSpirv> ShaderModule::GetHandleAndSpirv(
|
||||||
SingleShaderStage stage,
|
SingleShaderStage stage,
|
||||||
const ProgrammableStage& programmableStage,
|
const ProgrammableStage& programmableStage,
|
||||||
const PipelineLayout* layout) {
|
const PipelineLayout* layout,
|
||||||
|
bool clampFragDepth) {
|
||||||
TRACE_EVENT0(GetDevice()->GetPlatform(), General, "ShaderModuleVk::GetHandleAndSpirv");
|
TRACE_EVENT0(GetDevice()->GetPlatform(), General, "ShaderModuleVk::GetHandleAndSpirv");
|
||||||
|
|
||||||
// If the shader was destroyed, we should never call this function.
|
// If the shader was destroyed, we should never call this function.
|
||||||
|
@ -258,6 +260,7 @@ ResultOrError<ShaderModule::ModuleAndSpirv> ShaderModule::GetHandleAndSpirv(
|
||||||
req.disableSymbolRenaming = GetDevice()->IsToggleEnabled(Toggle::DisableSymbolRenaming);
|
req.disableSymbolRenaming = GetDevice()->IsToggleEnabled(Toggle::DisableSymbolRenaming);
|
||||||
req.useZeroInitializeWorkgroupMemoryExtension =
|
req.useZeroInitializeWorkgroupMemoryExtension =
|
||||||
GetDevice()->IsToggleEnabled(Toggle::VulkanUseZeroInitializeWorkgroupMemoryExtension);
|
GetDevice()->IsToggleEnabled(Toggle::VulkanUseZeroInitializeWorkgroupMemoryExtension);
|
||||||
|
req.clampFragDepth = clampFragDepth;
|
||||||
req.tracePlatform = UnsafeUnkeyedValue(GetDevice()->GetPlatform());
|
req.tracePlatform = UnsafeUnkeyedValue(GetDevice()->GetPlatform());
|
||||||
req.substituteOverrideConfig = std::move(substituteOverrideConfig);
|
req.substituteOverrideConfig = std::move(substituteOverrideConfig);
|
||||||
|
|
||||||
|
@ -305,6 +308,10 @@ ResultOrError<ShaderModule::ModuleAndSpirv> ShaderModule::GetHandleAndSpirv(
|
||||||
std::move(r.substituteOverrideConfig).value());
|
std::move(r.substituteOverrideConfig).value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (r.clampFragDepth) {
|
||||||
|
transformManager.Add<tint::transform::ClampFragDepth>();
|
||||||
|
}
|
||||||
|
|
||||||
tint::Program program;
|
tint::Program program;
|
||||||
tint::transform::DataMap transformOutputs;
|
tint::transform::DataMap transformOutputs;
|
||||||
{
|
{
|
||||||
|
|
|
@ -64,7 +64,8 @@ class ShaderModule final : public ShaderModuleBase {
|
||||||
|
|
||||||
ResultOrError<ModuleAndSpirv> GetHandleAndSpirv(SingleShaderStage stage,
|
ResultOrError<ModuleAndSpirv> GetHandleAndSpirv(SingleShaderStage stage,
|
||||||
const ProgrammableStage& programmableStage,
|
const ProgrammableStage& programmableStage,
|
||||||
const PipelineLayout* layout);
|
const PipelineLayout* layout,
|
||||||
|
bool clampFragDepth);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor);
|
ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor);
|
||||||
|
|
|
@ -506,6 +506,7 @@ source_set("end2end_tests_sources") {
|
||||||
"end2end/ExperimentalDP4aTests.cpp",
|
"end2end/ExperimentalDP4aTests.cpp",
|
||||||
"end2end/ExternalTextureTests.cpp",
|
"end2end/ExternalTextureTests.cpp",
|
||||||
"end2end/FirstIndexOffsetTests.cpp",
|
"end2end/FirstIndexOffsetTests.cpp",
|
||||||
|
"end2end/FragDepthTests.cpp",
|
||||||
"end2end/GpuMemorySynchronizationTests.cpp",
|
"end2end/GpuMemorySynchronizationTests.cpp",
|
||||||
"end2end/IndexFormatTests.cpp",
|
"end2end/IndexFormatTests.cpp",
|
||||||
"end2end/MaxLimitTests.cpp",
|
"end2end/MaxLimitTests.cpp",
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
// Copyright 2022 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 "dawn/tests/DawnTest.h"
|
||||||
|
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
|
||||||
|
#include "dawn/utils/WGPUHelpers.h"
|
||||||
|
|
||||||
|
constexpr wgpu::TextureFormat kDepthFormat = wgpu::TextureFormat::Depth32Float;
|
||||||
|
|
||||||
|
class FragDepthTests : public DawnTest {};
|
||||||
|
|
||||||
|
// Test that when writing to FragDepth the result is clamped to the viewport.
|
||||||
|
TEST_P(FragDepthTests, FragDepthIsClampedToViewport) {
|
||||||
|
// TODO(dawn:1125): Add the shader transform to clamp the frag depth to the GL backend.
|
||||||
|
DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
|
||||||
|
|
||||||
|
wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
|
||||||
|
@vertex fn vs() -> @builtin(position) vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 0.0, 0.5, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment fn fs() -> @builtin(frag_depth) f32 {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Create the pipeline that uses frag_depth to output the depth.
|
||||||
|
utils::ComboRenderPipelineDescriptor pDesc;
|
||||||
|
pDesc.vertex.module = module;
|
||||||
|
pDesc.vertex.entryPoint = "vs";
|
||||||
|
pDesc.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
pDesc.cFragment.module = module;
|
||||||
|
pDesc.cFragment.entryPoint = "fs";
|
||||||
|
pDesc.cFragment.targetCount = 0;
|
||||||
|
|
||||||
|
wgpu::DepthStencilState* pDescDS = pDesc.EnableDepthStencil(kDepthFormat);
|
||||||
|
pDescDS->depthWriteEnabled = true;
|
||||||
|
pDescDS->depthCompare = wgpu::CompareFunction::Always;
|
||||||
|
wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pDesc);
|
||||||
|
|
||||||
|
// Create a depth-only render pass.
|
||||||
|
wgpu::TextureDescriptor depthDesc;
|
||||||
|
depthDesc.size = {1, 1};
|
||||||
|
depthDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
||||||
|
depthDesc.format = kDepthFormat;
|
||||||
|
wgpu::Texture depthTexture = device.CreateTexture(&depthDesc);
|
||||||
|
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDesc({}, depthTexture.CreateView());
|
||||||
|
renderPassDesc.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
|
||||||
|
renderPassDesc.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
|
||||||
|
|
||||||
|
// Draw a point with a skewed viewport, so 1.0 depth gets clamped to 0.5.
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||||
|
pass.SetViewport(0, 0, 1, 1, 0.0, 0.5);
|
||||||
|
pass.SetPipeline(pipeline);
|
||||||
|
pass.Draw(1);
|
||||||
|
pass.End();
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
EXPECT_PIXEL_FLOAT_EQ(0.5f, depthTexture, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test for the push constant logic for ClampFragDepth in Vulkan to check that changing the
|
||||||
|
// pipeline layout doesn't invalidate the push constants that were set.
|
||||||
|
TEST_P(FragDepthTests, ChangingPipelineLayoutDoesntInvalidateViewport) {
|
||||||
|
// TODO(dawn:1125): Add the shader transform to clamp the frag depth to the GL backend.
|
||||||
|
DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
|
||||||
|
|
||||||
|
wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
|
||||||
|
@vertex fn vs() -> @builtin(position) vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 0.0, 0.5, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<uniform> uniformDepth : f32;
|
||||||
|
@fragment fn fsUniform() -> @builtin(frag_depth) f32 {
|
||||||
|
return uniformDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<storage, read> storageDepth : f32;
|
||||||
|
@fragment fn fsStorage() -> @builtin(frag_depth) f32 {
|
||||||
|
return storageDepth;
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Create the pipeline and bindgroup for the pipeline layout with a uniform buffer.
|
||||||
|
utils::ComboRenderPipelineDescriptor upDesc;
|
||||||
|
upDesc.vertex.module = module;
|
||||||
|
upDesc.vertex.entryPoint = "vs";
|
||||||
|
upDesc.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
upDesc.cFragment.module = module;
|
||||||
|
upDesc.cFragment.entryPoint = "fsUniform";
|
||||||
|
upDesc.cFragment.targetCount = 0;
|
||||||
|
|
||||||
|
wgpu::DepthStencilState* upDescDS = upDesc.EnableDepthStencil(kDepthFormat);
|
||||||
|
upDescDS->depthWriteEnabled = true;
|
||||||
|
upDescDS->depthCompare = wgpu::CompareFunction::Always;
|
||||||
|
wgpu::RenderPipeline uniformPipeline = device.CreateRenderPipeline(&upDesc);
|
||||||
|
|
||||||
|
wgpu::Buffer uniformBuffer =
|
||||||
|
utils::CreateBufferFromData<float>(device, wgpu::BufferUsage::Uniform, {0.0});
|
||||||
|
wgpu::BindGroup uniformBG =
|
||||||
|
utils::MakeBindGroup(device, uniformPipeline.GetBindGroupLayout(0), {{0, uniformBuffer}});
|
||||||
|
|
||||||
|
// Create the pipeline and bindgroup for the pipeline layout with a uniform buffer.
|
||||||
|
utils::ComboRenderPipelineDescriptor spDesc;
|
||||||
|
spDesc.vertex.module = module;
|
||||||
|
spDesc.vertex.entryPoint = "vs";
|
||||||
|
spDesc.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
spDesc.cFragment.module = module;
|
||||||
|
spDesc.cFragment.entryPoint = "fsStorage";
|
||||||
|
spDesc.cFragment.targetCount = 0;
|
||||||
|
|
||||||
|
wgpu::DepthStencilState* spDescDS = spDesc.EnableDepthStencil(kDepthFormat);
|
||||||
|
spDescDS->depthWriteEnabled = true;
|
||||||
|
spDescDS->depthCompare = wgpu::CompareFunction::Always;
|
||||||
|
wgpu::RenderPipeline storagePipeline = device.CreateRenderPipeline(&spDesc);
|
||||||
|
|
||||||
|
wgpu::Buffer storageBuffer =
|
||||||
|
utils::CreateBufferFromData<float>(device, wgpu::BufferUsage::Storage, {1.0});
|
||||||
|
wgpu::BindGroup storageBG =
|
||||||
|
utils::MakeBindGroup(device, storagePipeline.GetBindGroupLayout(0), {{0, storageBuffer}});
|
||||||
|
|
||||||
|
// Create a depth-only render pass.
|
||||||
|
wgpu::TextureDescriptor depthDesc;
|
||||||
|
depthDesc.size = {1, 1};
|
||||||
|
depthDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
||||||
|
depthDesc.format = kDepthFormat;
|
||||||
|
wgpu::Texture depthTexture = device.CreateTexture(&depthDesc);
|
||||||
|
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDesc({}, depthTexture.CreateView());
|
||||||
|
renderPassDesc.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
|
||||||
|
renderPassDesc.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
|
||||||
|
|
||||||
|
// Draw two point with a different pipeline layout to check Vulkan's behavior.
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||||
|
pass.SetViewport(0, 0, 1, 1, 0.0, 0.5);
|
||||||
|
|
||||||
|
// Writes 0.0.
|
||||||
|
pass.SetPipeline(uniformPipeline);
|
||||||
|
pass.SetBindGroup(0, uniformBG);
|
||||||
|
pass.Draw(1);
|
||||||
|
|
||||||
|
// Writes 1.0 clamped to 0.5.
|
||||||
|
pass.SetPipeline(storagePipeline);
|
||||||
|
pass.SetBindGroup(0, storageBG);
|
||||||
|
pass.Draw(1);
|
||||||
|
|
||||||
|
pass.End();
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
EXPECT_PIXEL_FLOAT_EQ(0.5f, depthTexture, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that if the fragment is outside of the viewport during rasterization, it is clipped
|
||||||
|
// even if it output @builtin(frag_depth).
|
||||||
|
TEST_P(FragDepthTests, RasterizationClipBeforeFS) {
|
||||||
|
// TODO(dawn:1616): Metal too needs to clamping of @builtin(frag_depth) to the viewport.
|
||||||
|
DAWN_SUPPRESS_TEST_IF(IsMetal());
|
||||||
|
|
||||||
|
wgpu::ShaderModule module = utils::CreateShaderModule(device, R"(
|
||||||
|
@vertex fn vs() -> @builtin(position) vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 0.0, 5.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment fn fs() -> @builtin(frag_depth) f32 {
|
||||||
|
return 0.5;
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Create the pipeline and bindgroup for the pipeline layout with a uniform buffer.
|
||||||
|
utils::ComboRenderPipelineDescriptor pDesc;
|
||||||
|
pDesc.vertex.module = module;
|
||||||
|
pDesc.vertex.entryPoint = "vs";
|
||||||
|
pDesc.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
pDesc.cFragment.module = module;
|
||||||
|
pDesc.cFragment.entryPoint = "fs";
|
||||||
|
pDesc.cFragment.targetCount = 0;
|
||||||
|
|
||||||
|
wgpu::DepthStencilState* pDescDS = pDesc.EnableDepthStencil(kDepthFormat);
|
||||||
|
pDescDS->depthWriteEnabled = true;
|
||||||
|
pDescDS->depthCompare = wgpu::CompareFunction::Always;
|
||||||
|
wgpu::RenderPipeline uniformPipeline = device.CreateRenderPipeline(&pDesc);
|
||||||
|
|
||||||
|
// Create a depth-only render pass.
|
||||||
|
wgpu::TextureDescriptor depthDesc;
|
||||||
|
depthDesc.size = {1, 1};
|
||||||
|
depthDesc.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
||||||
|
depthDesc.format = kDepthFormat;
|
||||||
|
wgpu::Texture depthTexture = device.CreateTexture(&depthDesc);
|
||||||
|
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDesc({}, depthTexture.CreateView());
|
||||||
|
renderPassDesc.cDepthStencilAttachmentInfo.depthClearValue = 0.0f;
|
||||||
|
renderPassDesc.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Undefined;
|
||||||
|
renderPassDesc.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Undefined;
|
||||||
|
|
||||||
|
// Draw a point with a depth outside of the viewport. It should get discarded.
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc);
|
||||||
|
pass.SetPipeline(uniformPipeline);
|
||||||
|
pass.Draw(1);
|
||||||
|
pass.End();
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands = encoder.Finish();
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
// The fragment should be discarded so the depth stayed 0.0, the depthClearValue.
|
||||||
|
EXPECT_PIXEL_FLOAT_EQ(0.0f, depthTexture, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DAWN_INSTANTIATE_TEST(FragDepthTests,
|
||||||
|
D3D12Backend(),
|
||||||
|
MetalBackend(),
|
||||||
|
OpenGLBackend(),
|
||||||
|
OpenGLESBackend(),
|
||||||
|
VulkanBackend());
|
|
@ -165,8 +165,7 @@ crbug.com/dawn/0000 webgpu:util,texture,texel_data:unorm_texel_data_in_shader:fo
|
||||||
################################################################################
|
################################################################################
|
||||||
# depth_clip_clamp failures
|
# depth_clip_clamp failures
|
||||||
################################################################################
|
################################################################################
|
||||||
crbug.com/dawn/1125 [ ubuntu ] webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:* [ Failure ]
|
crbug.com/dawn/1125 [ mac ] webgpu:api,operation,rendering,depth_clip_clamp:depth_clamp_and_clip:* [ Failure ]
|
||||||
crbug.com/dawn/1125 [ ubuntu ] webgpu:api,operation,rendering,depth_clip_clamp:depth_test_input_clamped:* [ Failure ]
|
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
# compilation_info failures
|
# compilation_info failures
|
||||||
|
|
Loading…
Reference in New Issue