1 /*
2 * Copyright 2020 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/d3d/GrD3DResourceProvider.h"
9
10 #include "include/gpu/GrContextOptions.h"
11 #include "include/gpu/GrDirectContext.h"
12 #include "include/private/SkOpts_spi.h"
13 #include "src/gpu/GrDirectContextPriv.h"
14 #include "src/gpu/d3d/GrD3DBuffer.h"
15 #include "src/gpu/d3d/GrD3DCommandList.h"
16 #include "src/gpu/d3d/GrD3DGpu.h"
17 #include "src/gpu/d3d/GrD3DPipelineState.h"
18 #include "src/gpu/d3d/GrD3DPipelineStateBuilder.h"
19 #include "src/gpu/d3d/GrD3DRenderTarget.h"
20
GrD3DResourceProvider(GrD3DGpu * gpu)21 GrD3DResourceProvider::GrD3DResourceProvider(GrD3DGpu* gpu)
22 : fGpu(gpu)
23 , fCpuDescriptorManager(gpu)
24 , fDescriptorTableManager(gpu)
25 , fPipelineStateCache(new PipelineStateCache(gpu))
26 , fShaderResourceDescriptorTableCache(gpu)
27 , fSamplerDescriptorTableCache(gpu) {
28 }
29
destroyResources()30 void GrD3DResourceProvider::destroyResources() {
31 fSamplers.reset();
32
33 fPipelineStateCache->release();
34 }
35
findOrCreateDirectCommandList()36 std::unique_ptr<GrD3DDirectCommandList> GrD3DResourceProvider::findOrCreateDirectCommandList() {
37 if (fAvailableDirectCommandLists.count()) {
38 std::unique_ptr<GrD3DDirectCommandList> list =
39 std::move(fAvailableDirectCommandLists.back());
40 fAvailableDirectCommandLists.pop_back();
41 return list;
42 }
43 return GrD3DDirectCommandList::Make(fGpu);
44 }
45
recycleDirectCommandList(std::unique_ptr<GrD3DDirectCommandList> commandList)46 void GrD3DResourceProvider::recycleDirectCommandList(
47 std::unique_ptr<GrD3DDirectCommandList> commandList) {
48 commandList->reset();
49 fAvailableDirectCommandLists.push_back(std::move(commandList));
50 }
51
findOrCreateRootSignature(int numTextureSamplers,int numUAVs)52 sk_sp<GrD3DRootSignature> GrD3DResourceProvider::findOrCreateRootSignature(int numTextureSamplers,
53 int numUAVs) {
54 for (int i = 0; i < fRootSignatures.count(); ++i) {
55 if (fRootSignatures[i]->isCompatible(numTextureSamplers, numUAVs)) {
56 return fRootSignatures[i];
57 }
58 }
59
60 auto rootSig = GrD3DRootSignature::Make(fGpu, numTextureSamplers, numUAVs);
61 if (!rootSig) {
62 return nullptr;
63 }
64 fRootSignatures.push_back(rootSig);
65 return rootSig;
66 }
67
findOrCreateCommandSignature(GrD3DCommandSignature::ForIndexed indexed,unsigned int slot)68 sk_sp<GrD3DCommandSignature> GrD3DResourceProvider::findOrCreateCommandSignature(
69 GrD3DCommandSignature::ForIndexed indexed, unsigned int slot) {
70 for (int i = 0; i < fCommandSignatures.count(); ++i) {
71 if (fCommandSignatures[i]->isCompatible(indexed, slot)) {
72 return fCommandSignatures[i];
73 }
74 }
75
76 auto commandSig = GrD3DCommandSignature::Make(fGpu, indexed, slot);
77 if (!commandSig) {
78 return nullptr;
79 }
80 fCommandSignatures.push_back(commandSig);
81 return commandSig;
82 }
83
createRenderTargetView(ID3D12Resource * textureResource)84 GrD3DDescriptorHeap::CPUHandle GrD3DResourceProvider::createRenderTargetView(
85 ID3D12Resource* textureResource) {
86 return fCpuDescriptorManager.createRenderTargetView(fGpu, textureResource);
87 }
88
recycleRenderTargetView(const GrD3DDescriptorHeap::CPUHandle & rtvDescriptor)89 void GrD3DResourceProvider::recycleRenderTargetView(
90 const GrD3DDescriptorHeap::CPUHandle& rtvDescriptor) {
91 fCpuDescriptorManager.recycleRenderTargetView(rtvDescriptor);
92 }
93
createDepthStencilView(ID3D12Resource * textureResource)94 GrD3DDescriptorHeap::CPUHandle GrD3DResourceProvider::createDepthStencilView(
95 ID3D12Resource* textureResource) {
96 return fCpuDescriptorManager.createDepthStencilView(fGpu, textureResource);
97 }
98
recycleDepthStencilView(const GrD3DDescriptorHeap::CPUHandle & dsvDescriptor)99 void GrD3DResourceProvider::recycleDepthStencilView(
100 const GrD3DDescriptorHeap::CPUHandle& dsvDescriptor) {
101 fCpuDescriptorManager.recycleDepthStencilView(dsvDescriptor);
102 }
103
createConstantBufferView(ID3D12Resource * bufferResource,size_t offset,size_t size)104 GrD3DDescriptorHeap::CPUHandle GrD3DResourceProvider::createConstantBufferView(
105 ID3D12Resource* bufferResource, size_t offset, size_t size) {
106 return fCpuDescriptorManager.createConstantBufferView(fGpu, bufferResource, offset, size);
107 }
108
createShaderResourceView(ID3D12Resource * resource,unsigned int highestMip,unsigned int mipLevels)109 GrD3DDescriptorHeap::CPUHandle GrD3DResourceProvider::createShaderResourceView(
110 ID3D12Resource* resource, unsigned int highestMip, unsigned int mipLevels) {
111 return fCpuDescriptorManager.createShaderResourceView(fGpu, resource, highestMip, mipLevels);
112 }
113
createUnorderedAccessView(ID3D12Resource * resource,unsigned int mipSlice)114 GrD3DDescriptorHeap::CPUHandle GrD3DResourceProvider::createUnorderedAccessView(
115 ID3D12Resource* resource, unsigned int mipSlice) {
116 return fCpuDescriptorManager.createUnorderedAccessView(fGpu, resource, mipSlice);
117 }
118
recycleShaderView(const GrD3DDescriptorHeap::CPUHandle & view)119 void GrD3DResourceProvider::recycleShaderView(
120 const GrD3DDescriptorHeap::CPUHandle& view) {
121 fCpuDescriptorManager.recycleShaderView(view);
122 }
123
wrap_mode_to_d3d_address_mode(GrSamplerState::WrapMode wrapMode)124 static D3D12_TEXTURE_ADDRESS_MODE wrap_mode_to_d3d_address_mode(GrSamplerState::WrapMode wrapMode) {
125 switch (wrapMode) {
126 case GrSamplerState::WrapMode::kClamp:
127 return D3D12_TEXTURE_ADDRESS_MODE_CLAMP;
128 case GrSamplerState::WrapMode::kRepeat:
129 return D3D12_TEXTURE_ADDRESS_MODE_WRAP;
130 case GrSamplerState::WrapMode::kMirrorRepeat:
131 return D3D12_TEXTURE_ADDRESS_MODE_MIRROR;
132 case GrSamplerState::WrapMode::kClampToBorder:
133 return D3D12_TEXTURE_ADDRESS_MODE_BORDER;
134 }
135 SK_ABORT("Unknown wrap mode.");
136 }
137
d3d_filter(GrSamplerState sampler)138 static D3D12_FILTER d3d_filter(GrSamplerState sampler) {
139 switch (sampler.mipmapMode()) {
140 // When the mode is kNone we disable filtering using maxLOD.
141 case GrSamplerState::MipmapMode::kNone:
142 case GrSamplerState::MipmapMode::kNearest:
143 switch (sampler.filter()) {
144 case GrSamplerState::Filter::kNearest: return D3D12_FILTER_MIN_MAG_MIP_POINT;
145 case GrSamplerState::Filter::kLinear: return D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT;
146 }
147 SkUNREACHABLE;
148 case GrSamplerState::MipmapMode::kLinear:
149 switch (sampler.filter()) {
150 case GrSamplerState::Filter::kNearest: return D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR;
151 case GrSamplerState::Filter::kLinear: return D3D12_FILTER_MIN_MAG_MIP_LINEAR;
152 }
153 SkUNREACHABLE;
154 }
155 SkUNREACHABLE;
156 }
157
findOrCreateCompatibleSampler(const GrSamplerState & params)158 D3D12_CPU_DESCRIPTOR_HANDLE GrD3DResourceProvider::findOrCreateCompatibleSampler(
159 const GrSamplerState& params) {
160 uint32_t key = params.asIndex();
161 D3D12_CPU_DESCRIPTOR_HANDLE* samplerPtr = fSamplers.find(key);
162 if (samplerPtr) {
163 return *samplerPtr;
164 }
165
166 D3D12_FILTER filter = d3d_filter(params);
167 // We disable MIP filtering using maxLOD. Otherwise, we want the max LOD to be unbounded.
168 float maxLOD = params.mipmapped() == GrMipmapped::kYes ? std::numeric_limits<float>::max()
169 : 0.f;
170 D3D12_TEXTURE_ADDRESS_MODE addressModeU = wrap_mode_to_d3d_address_mode(params.wrapModeX());
171 D3D12_TEXTURE_ADDRESS_MODE addressModeV = wrap_mode_to_d3d_address_mode(params.wrapModeY());
172
173 D3D12_CPU_DESCRIPTOR_HANDLE sampler =
174 fCpuDescriptorManager.createSampler(
175 fGpu, filter, maxLOD, addressModeU, addressModeV).fHandle;
176 fSamplers.set(key, sampler);
177 return sampler;
178 }
179
findOrCreateShaderViewTable(const std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> & shaderViews)180 sk_sp<GrD3DDescriptorTable> GrD3DResourceProvider::findOrCreateShaderViewTable(
181 const std::vector<D3D12_CPU_DESCRIPTOR_HANDLE>& shaderViews) {
182
183 auto createFunc = [this](GrD3DGpu* gpu, unsigned int numDesc) {
184 return this->fDescriptorTableManager.createShaderViewTable(gpu, numDesc);
185 };
186 return fShaderResourceDescriptorTableCache.findOrCreateDescTable(shaderViews, createFunc);
187 }
188
findOrCreateSamplerTable(const std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> & samplers)189 sk_sp<GrD3DDescriptorTable> GrD3DResourceProvider::findOrCreateSamplerTable(
190 const std::vector<D3D12_CPU_DESCRIPTOR_HANDLE>& samplers) {
191 auto createFunc = [this](GrD3DGpu* gpu, unsigned int numDesc) {
192 return this->fDescriptorTableManager.createSamplerTable(gpu, numDesc);
193 };
194 return fShaderResourceDescriptorTableCache.findOrCreateDescTable(samplers, createFunc);
195 }
196
findOrCreateCompatiblePipelineState(GrD3DRenderTarget * rt,const GrProgramInfo & info)197 GrD3DPipelineState* GrD3DResourceProvider::findOrCreateCompatiblePipelineState(
198 GrD3DRenderTarget* rt, const GrProgramInfo& info) {
199 return fPipelineStateCache->refPipelineState(rt, info);
200 }
201
findOrCreateMipmapPipeline()202 sk_sp<GrD3DPipeline> GrD3DResourceProvider::findOrCreateMipmapPipeline() {
203 if (!fMipmapPipeline) {
204 // Note: filtering for non-even widths and heights samples at the 0.25 and 0.75
205 // locations and averages the result. As the initial samples are bilerped this is
206 // approximately a triangle filter. We should look into doing a better kernel but
207 // this should hold us for now.
208 const char* shader =
209 "SamplerState textureSampler : register(s0, space1);\n"
210 "Texture2D<float4> inputTexture : register(t1, space1);\n"
211 "RWTexture2D<float4> outUAV : register(u2, space1);\n"
212 "\n"
213 "cbuffer UniformBuffer : register(b0, space0) {\n"
214 " float2 inverseDims;\n"
215 " uint mipLevel;\n"
216 " uint sampleMode;\n"
217 "}\n"
218 "\n"
219 "[numthreads(8, 8, 1)]\n"
220 "void main(uint groupIndex : SV_GroupIndex, uint3 threadID : SV_DispatchThreadID) {\n"
221 " float2 uv = inverseDims * (threadID.xy + 0.5);\n"
222 " float4 mipVal;\n"
223 " switch (sampleMode) {\n"
224 " case 0: {\n"
225 " mipVal = inputTexture.SampleLevel(textureSampler, uv, mipLevel);\n"
226 " break;\n"
227 " }\n"
228 " case 1: {\n"
229 " float2 uvdiff = inverseDims * 0.25;\n"
230 " mipVal = inputTexture.SampleLevel(textureSampler, uv-uvdiff, mipLevel);\n"
231 " mipVal += inputTexture.SampleLevel(textureSampler, uv+uvdiff, mipLevel);\n"
232 " uvdiff.y = -uvdiff.y;\n"
233 " mipVal += inputTexture.SampleLevel(textureSampler, uv-uvdiff, mipLevel);\n"
234 " mipVal += inputTexture.SampleLevel(textureSampler, uv+uvdiff, mipLevel);\n"
235 " mipVal *= 0.25;\n"
236 " break;\n"
237 " }\n"
238 " case 2: {\n"
239 " float2 uvdiff = float2(inverseDims.x * 0.25, 0);\n"
240 " mipVal = inputTexture.SampleLevel(textureSampler, uv-uvdiff, mipLevel);\n"
241 " mipVal += inputTexture.SampleLevel(textureSampler, uv+uvdiff, mipLevel);\n"
242 " mipVal *= 0.5;\n"
243 " break;\n"
244 " }\n"
245 " case 3: {\n"
246 " float2 uvdiff = float2(0, inverseDims.y * 0.25);\n"
247 " mipVal = inputTexture.SampleLevel(textureSampler, uv-uvdiff, mipLevel);\n"
248 " mipVal += inputTexture.SampleLevel(textureSampler, uv+uvdiff, mipLevel);\n"
249 " mipVal *= 0.5;\n"
250 " break;\n"
251 " }\n"
252 " }\n"
253 "\n"
254 " outUAV[threadID.xy] = mipVal;\n"
255 "}\n";
256
257 sk_sp<GrD3DRootSignature> rootSig = this->findOrCreateRootSignature(1, 1);
258
259 fMipmapPipeline =
260 GrD3DPipelineStateBuilder::MakeComputePipeline(fGpu, rootSig.get(), shader);
261 }
262
263 return fMipmapPipeline;
264 }
265
uploadConstantData(void * data,size_t size)266 D3D12_GPU_VIRTUAL_ADDRESS GrD3DResourceProvider::uploadConstantData(void* data, size_t size) {
267 // constant size has to be aligned to 256
268 constexpr int kConstantAlignment = 256;
269
270 // upload the data
271 size_t paddedSize = SkAlignTo(size, kConstantAlignment);
272 GrRingBuffer::Slice slice = fGpu->uniformsRingBuffer()->suballocate(paddedSize);
273 char* destPtr = static_cast<char*>(slice.fBuffer->map()) + slice.fOffset;
274 memcpy(destPtr, data, size);
275
276 // create the associated constant buffer view descriptor
277 GrD3DBuffer* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer);
278 D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = d3dBuffer->d3dResource()->GetGPUVirtualAddress();
279 return gpuAddress + slice.fOffset;
280 }
281
prepForSubmit()282 void GrD3DResourceProvider::prepForSubmit() {
283 fDescriptorTableManager.prepForSubmit(fGpu);
284 // Any heap memory used for these will be returned when the command buffer finishes,
285 // so we have to invalidate all entries.
286 fShaderResourceDescriptorTableCache.release();
287 fSamplerDescriptorTableCache.release();
288 }
289
290 ////////////////////////////////////////////////////////////////////////////////////////////////
291
292 #ifdef GR_PIPELINE_STATE_CACHE_STATS
293 // Display pipeline state cache usage
294 static const bool c_DisplayMtlPipelineCache{false};
295 #endif
296
297 struct GrD3DResourceProvider::PipelineStateCache::Entry {
EntryGrD3DResourceProvider::PipelineStateCache::Entry298 Entry(GrD3DGpu* gpu, std::unique_ptr<GrD3DPipelineState> pipelineState)
299 : fGpu(gpu), fPipelineState(std::move(pipelineState)) {}
300
301 GrD3DGpu* fGpu;
302 std::unique_ptr<GrD3DPipelineState> fPipelineState;
303 };
304
PipelineStateCache(GrD3DGpu * gpu)305 GrD3DResourceProvider::PipelineStateCache::PipelineStateCache(GrD3DGpu* gpu)
306 : fMap(gpu->getContext()->priv().options().fRuntimeProgramCacheSize)
307 , fGpu(gpu)
308 #ifdef GR_PIPELINE_STATE_CACHE_STATS
309 , fTotalRequests(0)
310 , fCacheMisses(0)
311 #endif
312 {
313 }
314
~PipelineStateCache()315 GrD3DResourceProvider::PipelineStateCache::~PipelineStateCache() {
316 // dump stats
317 #ifdef GR_PIPELINE_STATE_CACHE_STATS
318 if (c_DisplayMtlPipelineCache) {
319 SkDebugf("--- Pipeline State Cache ---\n");
320 SkDebugf("Total requests: %d\n", fTotalRequests);
321 SkDebugf("Cache misses: %d\n", fCacheMisses);
322 SkDebugf("Cache miss %%: %f\n",
323 (fTotalRequests > 0) ? 100.f * fCacheMisses / fTotalRequests : 0.f);
324 SkDebugf("---------------------\n");
325 }
326 #endif
327 }
328
release()329 void GrD3DResourceProvider::PipelineStateCache::release() {
330 fMap.reset();
331 }
332
refPipelineState(GrD3DRenderTarget * renderTarget,const GrProgramInfo & programInfo)333 GrD3DPipelineState* GrD3DResourceProvider::PipelineStateCache::refPipelineState(
334 GrD3DRenderTarget* renderTarget, const GrProgramInfo& programInfo) {
335 #ifdef GR_PIPELINE_STATE_CACHE_STATS
336 ++fTotalRequests;
337 #endif
338
339 const GrCaps* caps = fGpu->caps();
340
341 GrProgramDesc desc = caps->makeDesc(renderTarget, programInfo);
342 if (!desc.isValid()) {
343 GrCapsDebugf(fGpu->caps(), "Failed to build mtl program descriptor!\n");
344 return nullptr;
345 }
346
347 std::unique_ptr<Entry>* entry = fMap.find(desc);
348 if (!entry) {
349 #ifdef GR_PIPELINE_STATE_CACHE_STATS
350 ++fCacheMisses;
351 #endif
352 std::unique_ptr<GrD3DPipelineState> pipelineState =
353 GrD3DPipelineStateBuilder::MakePipelineState(fGpu, renderTarget, desc, programInfo);
354 if (!pipelineState) {
355 return nullptr;
356 }
357 entry = fMap.insert(desc, std::unique_ptr<Entry>(
358 new Entry(fGpu, std::move(pipelineState))));
359 return ((*entry)->fPipelineState).get();
360 }
361 return ((*entry)->fPipelineState).get();
362 }
363
markPipelineStateUniformsDirty()364 void GrD3DResourceProvider::PipelineStateCache::markPipelineStateUniformsDirty() {
365 fMap.foreach ([](const GrProgramDesc*, std::unique_ptr<Entry>* entry) {
366 (*entry)->fPipelineState->markUniformsDirty();
367 });
368 }
369
370 ////////////////////////////////////////////////////////////////////////////////////////////////
371
release()372 void GrD3DResourceProvider::DescriptorTableCache::release() {
373 fMap.reset();
374 }
375
findOrCreateDescTable(const std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> & cpuDescriptors,std::function<sk_sp<GrD3DDescriptorTable> (GrD3DGpu *,unsigned int numDesc)> createFunc)376 sk_sp<GrD3DDescriptorTable> GrD3DResourceProvider::DescriptorTableCache::findOrCreateDescTable(
377 const std::vector<D3D12_CPU_DESCRIPTOR_HANDLE>& cpuDescriptors,
378 std::function<sk_sp<GrD3DDescriptorTable>(GrD3DGpu*, unsigned int numDesc)> createFunc) {
379 sk_sp<GrD3DDescriptorTable>* entry = fMap.find(cpuDescriptors);
380 if (entry) {
381 return *entry;
382 }
383
384 unsigned int numDescriptors = cpuDescriptors.size();
385 SkASSERT(numDescriptors <= kRangeSizesCount);
386 sk_sp<GrD3DDescriptorTable> descTable = createFunc(fGpu, numDescriptors);
387 fGpu->device()->CopyDescriptors(1, descTable->baseCpuDescriptorPtr(), &numDescriptors,
388 numDescriptors, cpuDescriptors.data(), fRangeSizes,
389 descTable->type());
390 entry = fMap.insert(cpuDescriptors, std::move(descTable));
391 return *entry;
392 }
393