Skip to content

Commit

Permalink
[WebGPU] Clamp depth values to the viewport range
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=264449
<radar://118144869>

Reviewed by Tadeu Zagallo.

Reland PR with regression addressed.

Add passing expectations for api,operation,rendering,depth_clip_clamp
and add depth clamping when the viewport is not the default [0, 1] range.

* LayoutTests/http/tests/webgpu/webgpu/api/operation/rendering/depth_clip_clamp-expected.txt:
* Source/WebGPU/WGSL/Metal/MetalFunctionWriter.cpp:
(WGSL::Metal::FunctionDefinitionWriter::visit):
* Source/WebGPU/WebGPU/CommandEncoder.h:
* Source/WebGPU/WebGPU/CommandEncoder.mm:
(WebGPU::CommandEncoder::runDepthClampEncoder):
(WebGPU::CommandEncoder::beginRenderPass):
* Source/WebGPU/WebGPU/RenderPassEncoder.h:
* Source/WebGPU/WebGPU/RenderPassEncoder.mm:
(WebGPU::RenderPassEncoder::RenderPassEncoder):
(WebGPU::RenderPassEncoder::quantizedDepthValue):
(WebGPU::RenderPassEncoder::setPipeline):
(WebGPU::RenderPassEncoder::setViewport):

* Source/WebGPU/WGSL/GlobalVariableRewriter.cpp:
(WGSL::RewriteGlobalVariables::insertParameter):
(WGSL::RewriteGlobalVariables::visitEntryPoint):
(WGSL::RewriteGlobalVariables::insertDynamicOffsetsBufferIfNeeded):
(WGSL::RewriteGlobalVariables::insertParameters):
The staging buffer needs to be emitted whenever frag depth is possibly used.

There is no harm in always emitting it as we always call setFragmentBytes
on the last buffer index.

Canonical link: https://commits.webkit.org/274856@main
  • Loading branch information
mwyrzykowski committed Feb 16, 2024
1 parent c75cf46 commit 57f26e7
Show file tree
Hide file tree
Showing 8 changed files with 240 additions and 52 deletions.
Original file line number Diff line number Diff line change
@@ -1 +1,82 @@
(Populate me when we're ready to investigate this test)

PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth="_undef_";writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth="_undef_";writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth="_undef_";writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth="_undef_";writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=false;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=false;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=false;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=false;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=true;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=true;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=true;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth16unorm";unclippedDepth=true;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth="_undef_";writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth="_undef_";writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth="_undef_";writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth="_undef_";writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=false;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=false;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=false;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=false;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=true;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=true;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=true;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float";unclippedDepth=true;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth="_undef_";writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth="_undef_";writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth="_undef_";writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth="_undef_";writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=false;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=false;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=false;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=false;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=true;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=true;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=true;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus";unclippedDepth=true;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth="_undef_";writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth="_undef_";writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth="_undef_";writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth="_undef_";writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=false;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=false;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=false;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=false;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=true;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=true;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=true;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth24plus-stencil8";unclippedDepth=true;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth="_undef_";writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth="_undef_";writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth="_undef_";writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth="_undef_";writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=false;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=false;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=false;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=false;writeDepth=true;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=true;writeDepth=false;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=true;writeDepth=false;multisampled=true
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=true;writeDepth=true;multisampled=false
PASS :depth_clamp_and_clip:format="depth32float-stencil8";unclippedDepth=true;writeDepth=true;multisampled=true
PASS :depth_test_input_clamped:format="depth16unorm";unclippedDepth=false;multisampled=false
PASS :depth_test_input_clamped:format="depth16unorm";unclippedDepth=false;multisampled=true
PASS :depth_test_input_clamped:format="depth16unorm";unclippedDepth=true;multisampled=false
PASS :depth_test_input_clamped:format="depth16unorm";unclippedDepth=true;multisampled=true
PASS :depth_test_input_clamped:format="depth32float";unclippedDepth=false;multisampled=false
PASS :depth_test_input_clamped:format="depth32float";unclippedDepth=false;multisampled=true
PASS :depth_test_input_clamped:format="depth32float";unclippedDepth=true;multisampled=false
PASS :depth_test_input_clamped:format="depth32float";unclippedDepth=true;multisampled=true
PASS :depth_test_input_clamped:format="depth24plus";unclippedDepth=false;multisampled=false
PASS :depth_test_input_clamped:format="depth24plus";unclippedDepth=false;multisampled=true
PASS :depth_test_input_clamped:format="depth24plus";unclippedDepth=true;multisampled=false
PASS :depth_test_input_clamped:format="depth24plus";unclippedDepth=true;multisampled=true
PASS :depth_test_input_clamped:format="depth24plus-stencil8";unclippedDepth=false;multisampled=false
PASS :depth_test_input_clamped:format="depth24plus-stencil8";unclippedDepth=false;multisampled=true
PASS :depth_test_input_clamped:format="depth24plus-stencil8";unclippedDepth=true;multisampled=false
PASS :depth_test_input_clamped:format="depth24plus-stencil8";unclippedDepth=true;multisampled=true
PASS :depth_test_input_clamped:format="depth32float-stencil8";unclippedDepth=false;multisampled=false
PASS :depth_test_input_clamped:format="depth32float-stencil8";unclippedDepth=false;multisampled=true
PASS :depth_test_input_clamped:format="depth32float-stencil8";unclippedDepth=true;multisampled=false
PASS :depth_test_input_clamped:format="depth32float-stencil8";unclippedDepth=true;multisampled=true

74 changes: 48 additions & 26 deletions Source/WebGPU/WGSL/GlobalVariableRewriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ class RewriteGlobalVariables : public AST::Visitor {
AST::StructureMember& createArgumentBufferEntry(unsigned binding, AST::Variable&);
AST::StructureMember& createArgumentBufferEntry(unsigned binding, const SourceSpan&, const String& name, AST::Expression& type);
void finalizeArgumentBufferStruct(unsigned group, Vector<std::pair<unsigned, AST::StructureMember*>>&);
void insertDynamicOffsetsBufferIfNeeded(const AST::Function&);
void insertDynamicOffsetsBufferIfNeeded(const SourceSpan&, const AST::Function&);
void insertParameter(const SourceSpan&, const AST::Function&, unsigned, AST::Identifier&&, AST::Expression* = nullptr, AST::ParameterRole = AST::ParameterRole::BindGroup);

void insertParameters(AST::Function&, const Vector<unsigned>&);
void insertMaterializations(AST::Function&, const UsedResources&);
void insertLocalDefinitions(AST::Function&, const UsedPrivateGlobals&);
Expand Down Expand Up @@ -782,6 +786,25 @@ static size_t getRoundedSize(const AST::Variable& variable)
return roundUpToMultipleOf(16, type ? type->size() : 0);
}

void RewriteGlobalVariables::insertParameter(const SourceSpan& span, const AST::Function& function, unsigned group, AST::Identifier&& name, AST::Expression* type, AST::ParameterRole parameterRole)
{
if (!type) {
type = &m_callGraph.ast().astBuilder().construct<AST::IdentifierExpression>(span, argumentBufferStructName(group));
type->m_inferredType = m_structTypes.get(group);
}
auto& groupValue = m_callGraph.ast().astBuilder().construct<AST::AbstractIntegerLiteral>(span, group);
groupValue.m_inferredType = m_callGraph.ast().types().abstractIntType();
groupValue.setConstantValue(group);
auto& groupAttribute = m_callGraph.ast().astBuilder().construct<AST::GroupAttribute>(span, groupValue);
m_callGraph.ast().append(function.parameters(), m_callGraph.ast().astBuilder().construct<AST::Parameter>(
span,
WTFMove(name),
*type,
AST::Attribute::List { groupAttribute },
parameterRole
));
};

std::optional<Error> RewriteGlobalVariables::visitEntryPoint(const CallGraph::EntryPoint& entryPoint)
{
dataLogLnIf(shouldLogGlobalVariableRewriting, "> Visiting entrypoint: ", entryPoint.function.name());
Expand All @@ -806,16 +829,22 @@ std::optional<Error> RewriteGlobalVariables::visitEntryPoint(const CallGraph::En
}

visit(entryPoint.function);
if (m_reads.isEmpty())
if (m_reads.isEmpty()) {
insertDynamicOffsetsBufferIfNeeded(entryPoint.function);
return std::nullopt;
}

auto maybeUsedGlobals = determineUsedGlobals();
if (!maybeUsedGlobals)
if (!maybeUsedGlobals) {
insertDynamicOffsetsBufferIfNeeded(entryPoint.function);
return maybeUsedGlobals.error();
}
auto usedGlobals = *maybeUsedGlobals;
auto maybeGroups = m_generatedLayout ? Result<Vector<unsigned>>(insertStructs(usedGlobals.resources)) : insertStructs(*it->value, usedGlobals.resources);
if (!maybeGroups)
if (!maybeGroups) {
insertDynamicOffsetsBufferIfNeeded(entryPoint.function);
return maybeGroups.error();
}
insertParameters(entryPoint.function, *maybeGroups);
insertMaterializations(entryPoint.function, usedGlobals.resources);
insertLocalDefinitions(entryPoint.function, usedGlobals.privateGlobals);
Expand Down Expand Up @@ -1568,29 +1597,9 @@ Result<Vector<unsigned>> RewriteGlobalVariables::insertStructs(const PipelineLay
return { groups };
}

void RewriteGlobalVariables::insertParameters(AST::Function& function, const Vector<unsigned>& groups)
void RewriteGlobalVariables::insertDynamicOffsetsBufferIfNeeded(const SourceSpan& span, const AST::Function& function)
{
auto span = function.span();
const auto& insertParameter = [&](unsigned group, AST::Identifier&& name, AST::Expression* type = nullptr, AST::ParameterRole parameterRole = AST::ParameterRole::BindGroup) {
if (!type) {
type = &m_callGraph.ast().astBuilder().construct<AST::IdentifierExpression>(span, argumentBufferStructName(group));
type->m_inferredType = m_structTypes.get(group);
}
auto& groupValue = m_callGraph.ast().astBuilder().construct<AST::AbstractIntegerLiteral>(span, group);
groupValue.m_inferredType = m_callGraph.ast().types().abstractIntType();
groupValue.setConstantValue(group);
auto& groupAttribute = m_callGraph.ast().astBuilder().construct<AST::GroupAttribute>(span, groupValue);
m_callGraph.ast().append(function.parameters(), m_callGraph.ast().astBuilder().construct<AST::Parameter>(
span,
WTFMove(name),
*type,
AST::Attribute::List { groupAttribute },
parameterRole
));
};
for (auto group : groups)
insertParameter(group, argumentBufferParameterName(group));
if (!m_globalsUsingDynamicOffset.isEmpty()) {
if (!m_globalsUsingDynamicOffset.isEmpty() || (m_stage == ShaderStage::Fragment && m_callGraph.ast().usesFragDepth())) {
unsigned group;
switch (m_stage) {
case ShaderStage::Vertex:
Expand All @@ -1607,10 +1616,23 @@ void RewriteGlobalVariables::insertParameters(AST::Function& function, const Vec
auto& type = m_callGraph.ast().astBuilder().construct<AST::IdentifierExpression>(span, AST::Identifier::make("u32"_s));
type.m_inferredType = m_callGraph.ast().types().pointerType(AddressSpace::Uniform, m_callGraph.ast().types().u32Type(), AccessMode::Read);

insertParameter(group, AST::Identifier::make(dynamicOffsetVariableName()), &type, AST::ParameterRole::UserDefined);
insertParameter(span, function, group, AST::Identifier::make(dynamicOffsetVariableName()), &type, AST::ParameterRole::UserDefined);
}
}

void RewriteGlobalVariables::insertDynamicOffsetsBufferIfNeeded(const AST::Function& function)
{
insertDynamicOffsetsBufferIfNeeded(function.span(), function);
}

void RewriteGlobalVariables::insertParameters(AST::Function& function, const Vector<unsigned>& groups)
{
auto span = function.span();
for (auto group : groups)
insertParameter(span, function, group, argumentBufferParameterName(group));
insertDynamicOffsetsBufferIfNeeded(span, function);
}

void RewriteGlobalVariables::insertMaterializations(AST::Function& function, const UsedResources& usedResources)
{
auto span = function.span();
Expand Down
45 changes: 44 additions & 1 deletion Source/WebGPU/WGSL/Metal/MetalFunctionWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class FunctionDefinitionWriter : public AST::Visitor {
Indentation<4> m_indent { 0 };
std::optional<AST::StructureRole> m_structRole;
std::optional<ShaderStage> m_entryPointStage;
AST::Function* m_currentFunction { nullptr };
unsigned m_functionConstantIndex { 0 };
AST::Continuing*m_continuing { nullptr };
HashSet<AST::Function*> m_visitedFunctions;
Expand Down Expand Up @@ -470,12 +471,16 @@ void FunctionDefinitionWriter::visit(AST::Function& functionDefinition)
}
first = false;
}

// Clear the flag set while serializing StageAttribute
m_entryPointStage = std::nullopt;

m_currentFunction = &functionDefinition;
m_stringBuilder.append(")\n");
checkErrorAndVisit(functionDefinition.body());
m_stringBuilder.append("\n\n");

m_currentFunction = nullptr;
}

void FunctionDefinitionWriter::visit(AST::Structure& structDecl)
Expand Down Expand Up @@ -2232,13 +2237,51 @@ void FunctionDefinitionWriter::visit(AST::PhonyAssignmentStatement& statement)
m_stringBuilder.append(")");
}

static std::optional<std::pair<String, String>> fragDepthIdentifierForFunction(AST::Function* function)
{
if (!function || function->stage() != ShaderStage::Fragment)
return std::nullopt;

if (auto expression = function->maybeReturnType()) {
if (auto* inferredType = expression->inferredType()) {
auto& type = *inferredType;
auto* returnStruct = std::get_if<WGSL::Types::Struct>(&type);
if (!returnStruct)
return std::nullopt;

for (auto& member : returnStruct->structure.members()) {
if (member.builtin() == WGSL::Builtin::FragDepth)
return std::make_pair(returnStruct->structure.name(), member.name());
for (auto& attribute : member.attributes()) {
if (attribute.kind() != AST::NodeKind::BuiltinAttribute)
continue;
auto& builtinAttribute = downcast<AST::BuiltinAttribute>(attribute);
if (builtinAttribute.builtin() == WGSL::Builtin::FragDepth)
return std::make_pair(returnStruct->structure.name(), member.name());
}
}
}
}

return std::nullopt;
}

void FunctionDefinitionWriter::visit(AST::ReturnStatement& statement)
{
m_stringBuilder.append("return");
auto fragDepthIdentifier = fragDepthIdentifierForFunction(m_currentFunction);
if (fragDepthIdentifier)
m_stringBuilder.append(fragDepthIdentifier->first, " __wgslFragmentReturnResult = ");
else
m_stringBuilder.append("return");
if (statement.maybeExpression()) {
m_stringBuilder.append(" ");
visit(*statement.maybeExpression());
}

if (fragDepthIdentifier) {
m_stringBuilder.append(";\n__wgslFragmentReturnResult.", fragDepthIdentifier->second, " = clamp(__wgslFragmentReturnResult.", fragDepthIdentifier->second, ", as_type<float>(__DynamicOffsets[0]), as_type<float>(__DynamicOffsets[1]));\n");
m_stringBuilder.append("return __wgslFragmentReturnResult");
}
}

void FunctionDefinitionWriter::visit(AST::ForStatement& statement)
Expand Down
3 changes: 2 additions & 1 deletion Source/WebGPU/WebGPU/CommandEncoder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ static bool isRenderableTextureView(const TextureView& texture)
depthReadOnly = attachment->depthReadOnly;
if (hasDepthComponent) {
const auto& mtlAttachment = mtlDescriptor.depthAttachment;
mtlAttachment.clearDepth = std::min(1.f, std::max(0.f, attachment->depthClearValue));
auto clearDepth = std::clamp(RenderPassEncoder::quantizedDepthValue(attachment->depthClearValue, textureView.format()), 0., 1.);
mtlAttachment.clearDepth = clearDepth;
mtlAttachment.texture = metalDepthStencilTexture;
mtlAttachment.level = 0;
mtlAttachment.loadAction = loadAction(attachment->depthLoadOp, attachment->depthStoreOp);
Expand Down
5 changes: 5 additions & 0 deletions Source/WebGPU/WebGPU/RenderPassEncoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class RenderPassEncoder : public WGPURenderPassEncoderImpl, public RefCounted<Re
CommandEncoder& parentEncoder();
void setCommandEncoder(const BindGroupEntryUsageData::Resource&);
void addResourceToActiveResources(const BindGroupEntryUsageData::Resource&, id<MTLResource>, OptionSet<BindGroupEntryUsage>);
static double quantizedDepthValue(double, WGPUTextureFormat);

private:
RenderPassEncoder(id<MTLRenderCommandEncoder>, const WGPURenderPassDescriptor&, NSUInteger, bool depthReadOnly, bool stencilReadOnly, CommandEncoder&, id<MTLBuffer>, uint64_t maxDrawCount, Device&);
Expand Down Expand Up @@ -173,6 +174,10 @@ class RenderPassEncoder : public WGPURenderPassEncoderImpl, public RefCounted<Re
uint64_t m_drawCount { 0 };
const uint64_t m_maxDrawCount { 0 };
uint32_t m_stencilClearValue { 0 };
float m_viewportX { 0 };
float m_viewportY { 0 };
float m_viewportWidth { 0 };
float m_viewportHeight { 0 };
bool m_clearDepthAttachment { false };
bool m_clearStencilAttachment { false };
bool m_occlusionQueryActive { false };
Expand Down
Loading

0 comments on commit 57f26e7

Please sign in to comment.