summaryrefslogtreecommitdiff
path: root/tools/gfx/d3d11/d3d11-helper-functions.h
blob: cca49de149ba229d2562f59e087df8f01fed9014 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
// d3d11-helper-functions.h
#pragma once

#include "../../../source/core/slang-list.h"
#include "d3d11-base.h"
#include "slang-gfx.h"

namespace gfx
{

using namespace Slang;

namespace d3d11
{
/// Contextual data and operations required when binding shader objects to the pipeline state
struct BindingContext
{
    // One key service that the `BindingContext` provides is abstracting over
    // the difference between the D3D11 compute and graphics/rasteriation pipelines.
    // D3D11 has distinct operations for, e.g., `CSSetShaderResources`
    // for compute vs. `VSSetShaderResources` and `PSSetShaderResources`
    // for rasterization.
    //
    // The context type provides simple operations for setting each class
    // of resource/sampler, which will be overridden in derived types.
    //
    // TODO: These operations should really support binding multiple resources/samplers
    // in one call, so that we can eventually make more efficient use of the API.
    //
    // TODO: We could reasonably also just store the bound resources into
    // lcoal arrays like we are doing for UAVs, and remove the pipeline-specific
    // virtual functions. However, doing so would seemingly eliminate any
    // chance of avoiding redundant binding work when binding changes are
    // made for a root shader object.
    //
    virtual void setCBV(UINT index, ID3D11Buffer* buffer) = 0;
    virtual void setSRV(UINT index, ID3D11ShaderResourceView* srv) = 0;
    virtual void setSampler(UINT index, ID3D11SamplerState* sampler) = 0;

    // Unordered Access Views (UAVs) are a somewhat special case in that
    // the D3D11 API requires them to all be set at once, rather than one
    // at a time. To support this, we will keep a local array of the UAVs
    // that have been bound (up to the maximum supported by D3D 11.0)
    //
    void setUAV(UINT index, ID3D11UnorderedAccessView* uav)
    {
        uavs[index] = uav;

        // We will also track the total number of UAV slots that will
        // need to be bound (including any gaps that might occur due
        // to either explicit bindings or RTV bindings that conflict
        // with the `u` registers for fragment shaders).
        //
        if (uavCount <= index)
        {
            uavCount = index + 1;
        }
    }

    /// The values bound for any UAVs
    ID3D11UnorderedAccessView* uavs[D3D11_PS_CS_UAV_REGISTER_COUNT];

    /// The number of entries in `uavs` that need to be considered when binding to the pipeline
    UINT uavCount = 0;

    /// The D3D11 device that we are using for binding
    DeviceImpl* device = nullptr;

    /// The D3D11 device context that we are using for binding
    ID3D11DeviceContext* context = nullptr;

    /// Initialize a binding context for binding to the given `device` and `context`
    BindingContext(DeviceImpl* device, ID3D11DeviceContext* context)
        : device(device), context(context)
    {
        memset(uavs, 0, sizeof(uavs));
    }
};

/// A `BindingContext` for binding to the compute pipeline
struct ComputeBindingContext : BindingContext
{
    /// Initialize a binding context for binding to the given `device` and `context`
    ComputeBindingContext(DeviceImpl* device, ID3D11DeviceContext* context)
        : BindingContext(device, context)
    {
    }

    void setCBV(UINT index, ID3D11Buffer* buffer) SLANG_OVERRIDE
    {
        context->CSSetConstantBuffers(index, 1, &buffer);
    }

    void setSRV(UINT index, ID3D11ShaderResourceView* srv) SLANG_OVERRIDE
    {
        context->CSSetShaderResources(index, 1, &srv);
    }

    void setSampler(UINT index, ID3D11SamplerState* sampler) SLANG_OVERRIDE
    {
        context->CSSetSamplers(index, 1, &sampler);
    }
};

/// A `BindingContext` for binding to the graphics/rasterization pipeline
struct GraphicsBindingContext : BindingContext
{
    /// Initialize a binding context for binding to the given `device` and `context`
    GraphicsBindingContext(DeviceImpl* device, ID3D11DeviceContext* context)
        : BindingContext(device, context)
    {
    }

    // TODO: The operations here are only dealing with vertex and fragment
    // shaders for now. We should eventually extend them to handle HS/DS/GS
    // bindings. (We might want to skip those stages depending on whether
    // the associated program uses them at all).
    //
    // TODO: If we support cases where different stages might use distinct
    // entry-point parameters, we might need to support some modes where
    // a "stage mask" is passed in that applies to the bindings.
    //
    void setCBV(UINT index, ID3D11Buffer* buffer) SLANG_OVERRIDE
    {
        context->VSSetConstantBuffers(index, 1, &buffer);
        context->PSSetConstantBuffers(index, 1, &buffer);
    }

    void setSRV(UINT index, ID3D11ShaderResourceView* srv) SLANG_OVERRIDE
    {
        context->VSSetShaderResources(index, 1, &srv);
        context->PSSetShaderResources(index, 1, &srv);
    }

    void setSampler(UINT index, ID3D11SamplerState* sampler) SLANG_OVERRIDE
    {
        context->VSSetSamplers(index, 1, &sampler);
        context->PSSetSamplers(index, 1, &sampler);
    }
};

// In order to bind shader parameters to the correct locations, we need to
// be able to describe those locations. Most shader parameters will
// only consume a single type of D3D11-visible regsiter (e.g., a `t`
// register for a txture, or an `s` register for a sampler), and scalar
// integers suffice for these cases.
//
// In more complex cases we might be binding an entire "sub-object" like
// a parameter block, an entry point, etc. For the general case, we need
// to be able to represent a composite offset that includes offsets for
// each of the register classes known to D3D11.

/// A "simple" binding offset that records an offset in CBV/SRV/UAV/Sampler slots
struct SimpleBindingOffset
{
    uint32_t cbv = 0;
    uint32_t srv = 0;
    uint32_t uav = 0;
    uint32_t sampler = 0;

    /// Create a default (zero) offset
    SimpleBindingOffset() {}

    /// Create an offset based on offset information in the given Slang `varLayout`
    SimpleBindingOffset(slang::VariableLayoutReflection* varLayout)
    {
        if (varLayout)
        {
            cbv = (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
            srv = (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
            uav = (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
            sampler = (uint32_t)varLayout->getOffset(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
        }
    }

    /// Create an offset based on size/stride information in the given Slang `typeLayout`
    SimpleBindingOffset(slang::TypeLayoutReflection* typeLayout)
    {
        if (typeLayout)
        {
            cbv = (uint32_t)typeLayout->getSize(SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER);
            srv = (uint32_t)typeLayout->getSize(SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE);
            uav = (uint32_t)typeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS);
            sampler = (uint32_t)typeLayout->getSize(SLANG_PARAMETER_CATEGORY_SAMPLER_STATE);
        }
    }

    /// Add any values in the given `offset`
    void operator+=(SimpleBindingOffset const& offset)
    {
        cbv += offset.cbv;
        srv += offset.srv;
        uav += offset.uav;
        sampler += offset.sampler;
    }
};

// While a "simple" binding offset representation will work in many cases,
// once we need to deal with layout for programs with interface-type parameters
// that have been statically specialized, we also need to track the offset
// for where to bind any "pending" data that arises from the process of static
// specialization.
//
// In order to conveniently track both the "primary" and "pending" offset information,
// we will define a more complete `BindingOffset` type that combines simple
// binding offsets for the primary and pending parts.

/// A representation of the offset at which to bind a shader parameter or sub-object
struct BindingOffset : SimpleBindingOffset
{
    // Offsets for "primary" data are stored directly in the `BindingOffset`
    // via the inheritance from `SimpleBindingOffset`.

    /// Offset for any "pending" data
    SimpleBindingOffset pending;

    /// Create a default (zero) offset
    BindingOffset() {}

    /// Create an offset from a simple offset
    explicit BindingOffset(SimpleBindingOffset const& offset)
        : SimpleBindingOffset(offset)
    {
    }

    /// Create an offset based on offset information in the given Slang `varLayout`
    BindingOffset(slang::VariableLayoutReflection* varLayout)
        : SimpleBindingOffset(varLayout), pending(varLayout->getPendingDataLayout())
    {
    }

    /// Create an offset based on size/stride information in the given Slang `typeLayout`
    BindingOffset(slang::TypeLayoutReflection* typeLayout)
        : SimpleBindingOffset(typeLayout), pending(typeLayout->getPendingDataTypeLayout())
    {
    }

    /// Add any values in the given `offset`
    void operator+=(SimpleBindingOffset const& offset) { SimpleBindingOffset::operator+=(offset); }

    /// Add any values in the given `offset`
    void operator+=(BindingOffset const& offset)
    {
        SimpleBindingOffset::operator+=(offset);
        pending += offset.pending;
    }
};

bool isSupportedNVAPIOp(IUnknown* dev, uint32_t op);

D3D11_BIND_FLAG calcResourceFlag(ResourceState state);
int _calcResourceBindFlags(ResourceStateSet allowedStates);
int _calcResourceAccessFlags(MemoryType memType);

D3D11_FILTER_TYPE translateFilterMode(TextureFilteringMode mode);
D3D11_FILTER_REDUCTION_TYPE translateFilterReduction(TextureReductionOp op);
D3D11_TEXTURE_ADDRESS_MODE translateAddressingMode(TextureAddressingMode mode);
D3D11_COMPARISON_FUNC translateComparisonFunc(ComparisonFunc func);

D3D11_STENCIL_OP translateStencilOp(StencilOp op);
D3D11_FILL_MODE translateFillMode(FillMode mode);
D3D11_CULL_MODE translateCullMode(CullMode mode);
bool isBlendDisabled(AspectBlendDesc const& desc);
bool isBlendDisabled(TargetBlendDesc const& desc);
D3D11_BLEND_OP translateBlendOp(BlendOp op);
D3D11_BLEND translateBlendFactor(BlendFactor factor);
D3D11_COLOR_WRITE_ENABLE translateRenderTargetWriteMask(RenderTargetWriteMaskT mask);

void initSrvDesc(
    IResource::Type resourceType,
    const ITextureResource::Desc& textureDesc,
    DXGI_FORMAT pixelFormat,
    D3D11_SHADER_RESOURCE_VIEW_DESC& descOut);
} // namespace d3d11

Result SLANG_MCALL getD3D11Adapters(List<AdapterInfo>& outAdapters);

Result SLANG_MCALL createD3D11Device(const IDevice::Desc* desc, IDevice** outDevice);

} // namespace gfx