Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/API/Encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define OFFLOADTEST_API_ENCODER_H

#include "API/API.h"
#include "API/Enums.h"

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
Expand Down Expand Up @@ -105,6 +106,15 @@ class RenderEncoder : public CommandEncoder {
virtual void setVertexBuffer(uint32_t Slot, Buffer *VB, size_t Offset,
uint32_t Stride) = 0;

/// Override the pipeline's default per-draw shading rate. Backends that do
/// not implement Variable Rate Shading are free to ignore this; tests that
/// depend on it should mark themselves UNSUPPORTED on those backends.
virtual void setShadingRate(ShadingRate Rate) {}

/// Enable the SV_ShadingRate per-primitive input. Backends that do not
/// implement Variable Rate Shading Tier 2 are free to ignore this.
virtual void enablePrimitiveShadingRate() {}

virtual llvm::Error drawInstanced(const PipelineState &PSO,
uint32_t VertexCount,
uint32_t InstanceCount,
Expand Down
13 changes: 13 additions & 0 deletions include/API/Enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ enum class StoreAction {

enum class PrimitiveTopology { TriangleList, PointList, PatchList };

/// Per-draw rasterizer shading rate (D3D12 VRS Tier 1). Tier 1 hardware
/// supports the four base rates (1x1, 1x2, 2x1, 2x2); the additional rates
/// (2x4, 4x2, 4x4) require AdditionalShadingRatesSupported.
enum class ShadingRate {
Rate_1x1,
Rate_1x2,
Rate_2x1,
Rate_2x2,
Rate_2x4,
Rate_4x2,
Rate_4x4,
};

} // namespace offloadtest

#endif // OFFLOADTEST_API_ENUMS_H
16 changes: 16 additions & 0 deletions include/Support/Pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ struct Pipeline {
llvm::SmallVector<Result> Results;
llvm::SmallVector<DescriptorSet> Sets;
DispatchParametersSet DispatchParameters;
std::optional<ShadingRate> ShadingRateOverride;
bool PrimitiveShadingRate = false;
AccelerationStructureDescs AccelStructs;

uint32_t getVertexCount() const {
Expand Down Expand Up @@ -900,6 +902,20 @@ template <> struct ScalarEnumerationTraits<offloadtest::PrimitiveTopology> {
}
};

template <> struct ScalarEnumerationTraits<offloadtest::ShadingRate> {
static void enumeration(IO &I, offloadtest::ShadingRate &V) {
#define ENUM_CASE(Val, Yaml) I.enumCase(V, Yaml, offloadtest::ShadingRate::Val)
ENUM_CASE(Rate_1x1, "1x1");
ENUM_CASE(Rate_1x2, "1x2");
ENUM_CASE(Rate_2x1, "2x1");
ENUM_CASE(Rate_2x2, "2x2");
ENUM_CASE(Rate_2x4, "2x4");
ENUM_CASE(Rate_4x2, "4x2");
ENUM_CASE(Rate_4x4, "4x4");
#undef ENUM_CASE
}
};

template <> struct ScalarEnumerationTraits<offloadtest::dx::RootParamKind> {
static void enumeration(IO &I, offloadtest::dx::RootParamKind &V) {
#define ENUM_CASE(Val) I.enumCase(V, #Val, offloadtest::dx::RootParamKind::Val)
Expand Down
56 changes: 56 additions & 0 deletions lib/API/DX/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,42 @@ class DXRenderEncoder : public offloadtest::RenderEncoder {
ScissorSet = true;
}

void setShadingRate(offloadtest::ShadingRate Rate) override {
D3D12_SHADING_RATE DXRate = D3D12_SHADING_RATE_1X1;
switch (Rate) {
case offloadtest::ShadingRate::Rate_1x1:
DXRate = D3D12_SHADING_RATE_1X1;
break;
case offloadtest::ShadingRate::Rate_1x2:
DXRate = D3D12_SHADING_RATE_1X2;
break;
case offloadtest::ShadingRate::Rate_2x1:
DXRate = D3D12_SHADING_RATE_2X1;
break;
case offloadtest::ShadingRate::Rate_2x2:
DXRate = D3D12_SHADING_RATE_2X2;
break;
case offloadtest::ShadingRate::Rate_2x4:
DXRate = D3D12_SHADING_RATE_2X4;
break;
case offloadtest::ShadingRate::Rate_4x2:
DXRate = D3D12_SHADING_RATE_4X2;
break;
case offloadtest::ShadingRate::Rate_4x4:
DXRate = D3D12_SHADING_RATE_4X4;
break;
}
// Tier 1: no combiners (per-primitive / per-tile rates require Tier 2).
CB.CmdList->RSSetShadingRate(DXRate, nullptr);
}

void enablePrimitiveShadingRate() override {
const D3D12_SHADING_RATE_COMBINER Combiners[] = {
D3D12_SHADING_RATE_COMBINER_OVERRIDE,
D3D12_SHADING_RATE_COMBINER_PASSTHROUGH};
CB.CmdList->RSSetShadingRate(D3D12_SHADING_RATE_1X1, Combiners);
}

void setVertexBuffer(uint32_t Slot, offloadtest::Buffer *VB, size_t Offset,
uint32_t Stride) override {
assert(Slot < D3D12_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT &&
Expand Down Expand Up @@ -1506,6 +1542,21 @@ class DXDevice : public offloadtest::Device {
CD3DX12FeatureSupport Features;
Features.Init(Device.Get());

const bool SupportsVariableShadingRateTier1 =
Features.VariableShadingRateTier() >=
D3D12_VARIABLE_SHADING_RATE_TIER_1;
Caps.insert(
std::make_pair("VariableShadingRateTier1",
makeCapability<bool>("VariableShadingRateTier1",
SupportsVariableShadingRateTier1)));
const bool SupportsVariableShadingRateTier2 =
Features.VariableShadingRateTier() >=
D3D12_VARIABLE_SHADING_RATE_TIER_2;
Caps.insert(
std::make_pair("VariableShadingRateTier2",
makeCapability<bool>("VariableShadingRateTier2",
SupportsVariableShadingRateTier2)));

#define D3D_FEATURE_BOOL(Name) \
Caps.insert( \
std::make_pair(#Name, makeCapability<bool>(#Name, Features.Name())));
Expand Down Expand Up @@ -2348,6 +2399,11 @@ class DXDevice : public offloadtest::Device {
Scissor.Height = static_cast<uint32_t>(VP.Height);
Encoder.setScissor(Scissor);

if (P.ShadingRateOverride)
Encoder.setShadingRate(*P.ShadingRateOverride);
if (P.PrimitiveShadingRate)
Encoder.enablePrimitiveShadingRate();

if (P.isTraditionalRaster()) {
if (IS.VB)
Encoder.setVertexBuffer(0, IS.VB.get(), 0,
Expand Down
9 changes: 9 additions & 0 deletions lib/API/MTL/MTLDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1937,6 +1937,15 @@ class MTLDevice : public offloadtest::Device {
}

llvm::Error executeProgram(Pipeline &P) override {
// Variable Rate Shading is not yet implemented on Metal. Tests that set
// a per-draw or per-primitive shading rate should mark themselves
// `UNSUPPORTED: Metal` so this guard never fires in CI; it exists to
// keep the shared Pipeline contract honest (see #1044).
if (P.ShadingRateOverride || P.PrimitiveShadingRate)
return llvm::createStringError(
std::errc::not_supported,
"Variable Rate Shading is not yet implemented on Metal");

InvocationState IS;

auto CBOrErr = MTLCommandBuffer::create(GraphicsQueue.Queue);
Expand Down
9 changes: 9 additions & 0 deletions lib/API/VK/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3547,6 +3547,15 @@ class VulkanDevice : public offloadtest::Device {
}

llvm::Error executeProgram(Pipeline &P) override {
// Variable Rate Shading is not yet implemented on Vulkan. Tests that set
// a per-draw or per-primitive shading rate should mark themselves
// `UNSUPPORTED: Vulkan` so this guard never fires in CI; it exists to
// keep the shared Pipeline contract honest (see #1044).
if (P.ShadingRateOverride || P.PrimitiveShadingRate)
return llvm::createStringError(
std::errc::not_supported,
"Variable Rate Shading is not yet implemented on Vulkan");

InvocationState State;
auto CleanupState = llvm::scope_exit([&]() {
cleanup(State);
Expand Down
3 changes: 3 additions & 0 deletions lib/Support/Pipeline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ void MappingTraits<offloadtest::Pipeline>::mapping(IO &I,
if (auto Err = P.validateDispatchParameters())
I.setError(llvm::toString(std::move(Err)));

I.mapOptional("ShadingRate", P.ShadingRateOverride);
I.mapOptional("PrimitiveShadingRate", P.PrimitiveShadingRate, false);

if (!I.outputting()) {
for (auto &D : P.Sets) {
for (auto &R : D.Resources) {
Expand Down
84 changes: 84 additions & 0 deletions test/Feature/VRS/draw-rate-2x2.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#--- vertex.hlsl
struct VSInput {
float4 position : POSITION;
};

struct VSOutput {
float4 position : SV_POSITION;
};

VSOutput main(VSInput input) {
VSOutput output;
output.position = input.position;
return output;
}

#--- pixel.hlsl
// Encode the per-quad screen-space derivative of SV_Position into the output
// color. Without VRS the derivative is 1 (one pixel per shading invocation)
// and red == 1/4 == 0.25. With a 2x2 coarse rate, each shading invocation
// covers 2x2 pixels, so the derivative across the shading quad is 2 fine
// pixels and red == 2/4 == 0.5 (similarly for green from ddy).
float4 main(float4 pos : SV_Position) : SV_Target {
return float4(ddx_coarse(pos.x) / 4.0,
ddy_coarse(pos.y) / 4.0,
0.0, 1.0);
}

#--- pipeline.yaml
---
Shaders:
- Stage: Vertex
Entry: main
- Stage: Pixel
Entry: main
Buffers:
- Name: VertexData
Format: Float32
Stride: 16
Data: [ 0.0, 3.0, 0.0, 1.0,
3.0, -3.0, 0.0, 1.0,
-3.0, -3.0, 0.0, 1.0 ]
- Name: Output
Format: Float32
Channels: 4
FillSize: 256 # 4x4 @ 16 bytes per pixel
OutputProps:
Height: 4
Width: 4
Depth: 1
Bindings:
VertexBuffer: VertexData
VertexAttributes:
- Format: Float32
Channels: 4
Offset: 0
Name: POSITION
RenderTarget: Output
DescriptorSets: []
ShadingRate: 2x2
...
#--- end

# Variable Rate Shading Tier 1 (per-draw RSSetShadingRate) is DirectX-only
# today. WARP supports the API on Windows 10 1903+.
# UNSUPPORTED: Vulkan || Metal
# REQUIRES: VariableShadingRateTier1
# XFAIL: Clang

# RUN: split-file %s %t
# RUN: %dxc_target -T vs_6_0 -Fo %t-vertex.o %t/vertex.hlsl
# RUN: %dxc_target -T ps_6_0 -Fo %t-pixel.o %t/pixel.hlsl
# RUN: %offloader %t/pipeline.yaml %t-vertex.o %t-pixel.o | FileCheck %s

# All 16 output pixels share the (0.5, 0.5, 0, 1) value computed by the
# coarse shading invocations, confirming the 2x2 rate took effect.
# CHECK: Name: Output
# CHECK-NEXT: Format: Float32
# CHECK-NEXT: Channels: 4
# CHECK-NEXT: Data: [ 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1 ]
83 changes: 83 additions & 0 deletions test/Feature/VRS/primitive-rate-2x2.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#--- vertex.hlsl
struct VSInput {
float4 position : POSITION;
};

struct VSOutput {
float4 position : SV_POSITION;
uint shadingRate : SV_ShadingRate;
};

VSOutput main(VSInput input) {
VSOutput output;
output.position = input.position;
output.shadingRate = 0x5; // D3D12_SHADING_RATE_2X2
return output;
}

#--- pixel.hlsl
// Encode the per-quad screen-space derivative of SV_Position into the output
// color. With a per-primitive 2x2 shading rate from SV_ShadingRate, each
// shading invocation covers 2x2 pixels, so both derivatives are 2 fine pixels.
float4 main(float4 pos : SV_Position) : SV_Target {
return float4(ddx_coarse(pos.x) / 4.0,
ddy_coarse(pos.y) / 4.0,
0.0, 1.0);
}

#--- pipeline.yaml
---
Shaders:
- Stage: Vertex
Entry: main
- Stage: Pixel
Entry: main
Buffers:
- Name: VertexData
Format: Float32
Stride: 16
Data: [ 0.0, 3.0, 0.0, 1.0,
3.0, -3.0, 0.0, 1.0,
-3.0, -3.0, 0.0, 1.0 ]
- Name: Output
Format: Float32
Channels: 4
FillSize: 256 # 4x4 @ 16 bytes per pixel
OutputProps:
Height: 4
Width: 4
Depth: 1
Bindings:
VertexBuffer: VertexData
VertexAttributes:
- Format: Float32
Channels: 4
Offset: 0
Name: POSITION
RenderTarget: Output
DescriptorSets: []
PrimitiveShadingRate: true
...
#--- end

# Variable Rate Shading Tier 2 (SV_ShadingRate) is DirectX-only today.
# UNSUPPORTED: Vulkan || Metal
# REQUIRES: VariableShadingRateTier2
# XFAIL: Clang

# RUN: split-file %s %t
# RUN: %dxc_target -T vs_6_4 -Fo %t-vertex.o %t/vertex.hlsl
# RUN: %dxc_target -T ps_6_0 -Fo %t-pixel.o %t/pixel.hlsl
# RUN: %offloader %t/pipeline.yaml %t-vertex.o %t-pixel.o | FileCheck %s

# All 16 output pixels share the (0.5, 0.5, 0, 1) value computed by the
# coarse shading invocations, confirming the per-primitive 2x2 rate took effect.
# CHECK: Name: Output
# CHECK-NEXT: Format: Float32
# CHECK-NEXT: Channels: 4
# CHECK-NEXT: Data: [ 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1, 0.5, 0.5, 0, 1,
# CHECK-NEXT: 0.5, 0.5, 0, 1 ]
4 changes: 4 additions & 0 deletions test/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ def setDeviceFeatures(config, device, compiler):
config.available_features.add("Int64TypedResourceAtomics")
if device["Features"].get("MeshShaderTier", "NotSupported") != "NotSupported":
config.available_features.add("MeshShader")
if device["Features"].get("VariableShadingRateTier1", False):
config.available_features.add("VariableShadingRateTier1")
if device["Features"].get("VariableShadingRateTier2", False):
config.available_features.add("VariableShadingRateTier2")
setWaveSizeFeaturesDirectX(config, device)
if device["Features"].get("RaytracingTier", "NotSupported") != "NotSupported":
config.available_features.add("acceleration-structure")
Expand Down
Loading