1/*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#include "d3d12_context.h"
25#include "d3d12_format.h"
26#include "d3d12_resource.h"
27#include "d3d12_screen.h"
28#include "d3d12_surface.h"
29
30#include "util/format/u_format.h"
31#include "util/u_inlines.h"
32#include "util/u_memory.h"
33
34static D3D12_DSV_DIMENSION
35view_dsv_dimension(enum pipe_texture_target target, unsigned samples)
36{
37   switch (target) {
38   case PIPE_TEXTURE_1D: return D3D12_DSV_DIMENSION_TEXTURE1D;
39   case PIPE_TEXTURE_1D_ARRAY: return D3D12_DSV_DIMENSION_TEXTURE1DARRAY;
40
41   case PIPE_TEXTURE_2D:
42   case PIPE_TEXTURE_RECT:
43      return samples > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS :
44                           D3D12_DSV_DIMENSION_TEXTURE2D;
45
46   case PIPE_TEXTURE_2D_ARRAY:
47   case PIPE_TEXTURE_CUBE:
48   case PIPE_TEXTURE_CUBE_ARRAY:
49      return samples > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY :
50                           D3D12_DSV_DIMENSION_TEXTURE2DARRAY;
51
52   default:
53      unreachable("unexpected target");
54   }
55}
56
57static D3D12_RTV_DIMENSION
58view_rtv_dimension(enum pipe_texture_target target, unsigned samples)
59{
60   switch (target) {
61   case PIPE_BUFFER: return D3D12_RTV_DIMENSION_BUFFER;
62   case PIPE_TEXTURE_1D: return D3D12_RTV_DIMENSION_TEXTURE1D;
63   case PIPE_TEXTURE_1D_ARRAY: return D3D12_RTV_DIMENSION_TEXTURE1DARRAY;
64
65   case PIPE_TEXTURE_2D:
66   case PIPE_TEXTURE_RECT:
67      return samples > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMS :
68                           D3D12_RTV_DIMENSION_TEXTURE2D;
69
70   case PIPE_TEXTURE_2D_ARRAY:
71   case PIPE_TEXTURE_CUBE:
72   case PIPE_TEXTURE_CUBE_ARRAY:
73      return samples > 1 ? D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY :
74                           D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
75
76   case PIPE_TEXTURE_3D: return D3D12_RTV_DIMENSION_TEXTURE3D;
77
78   default:
79      unreachable("unexpected target");
80   }
81}
82
83static void
84initialize_dsv(struct pipe_context *pctx,
85               struct pipe_resource *pres,
86               const struct pipe_surface *tpl,
87               struct d3d12_descriptor_handle *handle,
88               DXGI_FORMAT dxgi_format)
89{
90   struct d3d12_resource *res = d3d12_resource(pres);
91   struct d3d12_screen *screen = d3d12_screen(pctx->screen);
92
93   D3D12_DEPTH_STENCIL_VIEW_DESC desc;
94   desc.Format = dxgi_format;
95   desc.Flags = D3D12_DSV_FLAG_NONE;
96
97   desc.ViewDimension = view_dsv_dimension(pres->target, pres->nr_samples);
98   switch (desc.ViewDimension) {
99   case D3D12_DSV_DIMENSION_TEXTURE1D:
100      if (tpl->u.tex.first_layer > 0)
101         debug_printf("D3D12: can't create 1D DSV from layer %d\n",
102                      tpl->u.tex.first_layer);
103
104      desc.Texture1D.MipSlice = tpl->u.tex.level;
105      break;
106
107   case D3D12_DSV_DIMENSION_TEXTURE1DARRAY:
108      desc.Texture1DArray.MipSlice = tpl->u.tex.level;
109      desc.Texture1DArray.FirstArraySlice = tpl->u.tex.first_layer;
110      desc.Texture1DArray.ArraySize = tpl->u.tex.last_layer - tpl->u.tex.first_layer + 1;
111      break;
112
113   case D3D12_DSV_DIMENSION_TEXTURE2DMS:
114      if (tpl->u.tex.first_layer > 0)
115         debug_printf("D3D12: can't create 2DMS DSV from layer %d\n",
116                      tpl->u.tex.first_layer);
117
118      break;
119
120   case D3D12_DSV_DIMENSION_TEXTURE2D:
121      if (tpl->u.tex.first_layer > 0)
122         debug_printf("D3D12: can't create 2D DSV from layer %d\n",
123                      tpl->u.tex.first_layer);
124
125      desc.Texture2D.MipSlice = tpl->u.tex.level;
126      break;
127
128   case D3D12_DSV_DIMENSION_TEXTURE2DMSARRAY:
129      desc.Texture2DMSArray.FirstArraySlice = tpl->u.tex.first_layer;
130      desc.Texture2DMSArray.ArraySize = tpl->u.tex.last_layer - tpl->u.tex.first_layer + 1;
131      break;
132
133   case D3D12_DSV_DIMENSION_TEXTURE2DARRAY:
134      desc.Texture2DArray.MipSlice = tpl->u.tex.level;
135      desc.Texture2DArray.FirstArraySlice = tpl->u.tex.first_layer;
136      desc.Texture2DArray.ArraySize = tpl->u.tex.last_layer - tpl->u.tex.first_layer + 1;
137      break;
138
139   default:
140      unreachable("Unhandled DSV dimension");
141   }
142
143   mtx_lock(&screen->descriptor_pool_mutex);
144   d3d12_descriptor_pool_alloc_handle(screen->dsv_pool, handle);
145   mtx_unlock(&screen->descriptor_pool_mutex);
146
147   screen->dev->CreateDepthStencilView(d3d12_resource_resource(res), &desc,
148                                       handle->cpu_handle);
149}
150
151static void
152initialize_rtv(struct pipe_context *pctx,
153               struct pipe_resource *pres,
154               const struct pipe_surface *tpl,
155               struct d3d12_descriptor_handle *handle,
156               DXGI_FORMAT dxgi_format)
157{
158   struct d3d12_resource *res = d3d12_resource(pres);
159   struct d3d12_screen *screen = d3d12_screen(pctx->screen);
160
161   D3D12_RENDER_TARGET_VIEW_DESC desc;
162   desc.Format = dxgi_format;
163
164   desc.ViewDimension = view_rtv_dimension(pres->target, pres->nr_samples);
165   switch (desc.ViewDimension) {
166   case D3D12_RTV_DIMENSION_BUFFER:
167      desc.Buffer.FirstElement = 0;
168      desc.Buffer.NumElements = pres->width0 / util_format_get_blocksize(tpl->format);
169      break;
170
171   case D3D12_RTV_DIMENSION_TEXTURE1D:
172      if (tpl->u.tex.first_layer > 0)
173         debug_printf("D3D12: can't create 1D RTV from layer %d\n",
174                      tpl->u.tex.first_layer);
175
176      desc.Texture1D.MipSlice = tpl->u.tex.level;
177      break;
178
179   case D3D12_RTV_DIMENSION_TEXTURE1DARRAY:
180      desc.Texture1DArray.MipSlice = tpl->u.tex.level;
181      desc.Texture1DArray.FirstArraySlice = tpl->u.tex.first_layer;
182      desc.Texture1DArray.ArraySize = tpl->u.tex.last_layer - tpl->u.tex.first_layer + 1;
183      break;
184
185   case D3D12_RTV_DIMENSION_TEXTURE2DMS:
186      if (tpl->u.tex.first_layer > 0)
187         debug_printf("D3D12: can't create 2DMS RTV from layer %d\n",
188                      tpl->u.tex.first_layer);
189      break;
190
191   case D3D12_RTV_DIMENSION_TEXTURE2D:
192      if (tpl->u.tex.first_layer > 0)
193         debug_printf("D3D12: can't create 2D RTV from layer %d\n",
194                      tpl->u.tex.first_layer);
195
196      desc.Texture2D.MipSlice = tpl->u.tex.level;
197      desc.Texture2D.PlaneSlice = res->plane_slice;
198      break;
199
200   case D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY:
201      desc.Texture2DMSArray.FirstArraySlice = tpl->u.tex.first_layer;
202      desc.Texture2DMSArray.ArraySize = tpl->u.tex.last_layer - tpl->u.tex.first_layer + 1;
203      break;
204
205   case D3D12_RTV_DIMENSION_TEXTURE2DARRAY:
206      desc.Texture2DArray.MipSlice = tpl->u.tex.level;
207      desc.Texture2DArray.FirstArraySlice = tpl->u.tex.first_layer;
208      desc.Texture2DArray.ArraySize = tpl->u.tex.last_layer - tpl->u.tex.first_layer + 1;
209      desc.Texture2DArray.PlaneSlice = 0;
210      break;
211
212   case D3D12_RTV_DIMENSION_TEXTURE3D:
213      desc.Texture3D.MipSlice = tpl->u.tex.level;
214      desc.Texture3D.FirstWSlice = tpl->u.tex.first_layer;
215      desc.Texture3D.WSize = tpl->u.tex.last_layer - tpl->u.tex.first_layer + 1;
216      break;
217
218   default:
219      unreachable("Unhandled RTV dimension");
220   }
221
222   mtx_lock(&screen->descriptor_pool_mutex);
223   d3d12_descriptor_pool_alloc_handle(screen->rtv_pool, handle);
224   mtx_unlock(&screen->descriptor_pool_mutex);
225
226   screen->dev->CreateRenderTargetView(d3d12_resource_resource(res), &desc,
227                                       handle->cpu_handle);
228}
229
230static struct pipe_surface *
231d3d12_create_surface(struct pipe_context *pctx,
232                     struct pipe_resource *pres,
233                     const struct pipe_surface *tpl)
234{
235   bool is_depth_or_stencil = util_format_is_depth_or_stencil(tpl->format);
236   unsigned bind = is_depth_or_stencil ? PIPE_BIND_DEPTH_STENCIL : PIPE_BIND_RENDER_TARGET;
237
238   /* Don't bother if we don't support the requested format as RT or DS */
239   if (!pctx->screen->is_format_supported(pctx->screen, tpl->format, PIPE_TEXTURE_2D,
240                                          tpl->nr_samples, tpl->nr_samples,bind))
241      return NULL;
242
243   struct d3d12_surface *surface = CALLOC_STRUCT(d3d12_surface);
244   if (!surface)
245      return NULL;
246
247   pipe_resource_reference(&surface->base.texture, pres);
248   pipe_reference_init(&surface->base.reference, 1);
249   surface->base.context = pctx;
250   surface->base.format = tpl->format;
251   surface->base.width = u_minify(pres->width0, tpl->u.tex.level);
252   surface->base.height = u_minify(pres->height0, tpl->u.tex.level);
253   surface->base.u.tex.level = tpl->u.tex.level;
254   surface->base.u.tex.first_layer = tpl->u.tex.first_layer;
255   surface->base.u.tex.last_layer = tpl->u.tex.last_layer;
256
257   DXGI_FORMAT dxgi_format = d3d12_get_resource_rt_format(tpl->format);
258   if (is_depth_or_stencil)
259      initialize_dsv(pctx, pres, tpl, &surface->desc_handle, dxgi_format);
260   else
261      initialize_rtv(pctx, pres, tpl, &surface->desc_handle, dxgi_format);
262
263   return &surface->base;
264}
265
266static void
267d3d12_surface_destroy(struct pipe_context *pctx,
268                      struct pipe_surface *psurf)
269{
270   struct d3d12_surface *surface = (struct d3d12_surface*) psurf;
271   struct d3d12_screen *screen = d3d12_screen(pctx->screen);
272
273   mtx_lock(&screen->descriptor_pool_mutex);
274   d3d12_descriptor_handle_free(&surface->desc_handle);
275   if (d3d12_descriptor_handle_is_allocated(&surface->uint_rtv_handle))
276      d3d12_descriptor_handle_free(&surface->uint_rtv_handle);
277   mtx_unlock(&screen->descriptor_pool_mutex);
278
279   pipe_resource_reference(&psurf->texture, NULL);
280   pipe_resource_reference(&surface->rgba_texture, NULL);
281   FREE(surface);
282}
283
284static void
285blit_surface(struct pipe_context *pctx, struct d3d12_surface *surface, bool pre)
286{
287   struct pipe_blit_info info = {};
288
289   info.src.resource = pre ? surface->base.texture : surface->rgba_texture;
290   info.dst.resource = pre ? surface->rgba_texture : surface->base.texture;
291   info.src.format = pre ? surface->base.texture->format : PIPE_FORMAT_R8G8B8A8_UNORM;
292   info.dst.format = pre ? PIPE_FORMAT_R8G8B8A8_UNORM : surface->base.texture->format;
293   info.src.level = info.dst.level = 0;
294   info.src.box.x = info.dst.box.x = 0;
295   info.src.box.y = info.dst.box.y = 0;
296   info.src.box.z = info.dst.box.z = 0;
297   info.src.box.width = info.dst.box.width = surface->base.width;
298   info.src.box.height = info.dst.box.height = surface->base.height;
299   info.src.box.depth = info.dst.box.depth = 0;
300   info.mask = PIPE_MASK_RGBA;
301
302   d3d12_blit(pctx, &info);
303}
304
305enum d3d12_surface_conversion_mode
306d3d12_surface_update_pre_draw(struct pipe_context *pctx,
307                              struct d3d12_surface *surface,
308                              DXGI_FORMAT format)
309{
310   struct d3d12_screen *screen = d3d12_screen(surface->base.context->screen);
311   struct d3d12_resource *res = d3d12_resource(surface->base.texture);
312   DXGI_FORMAT dxgi_format = d3d12_get_resource_rt_format(surface->base.format);
313   enum d3d12_surface_conversion_mode mode;
314
315   if (dxgi_format == format)
316      return D3D12_SURFACE_CONVERSION_NONE;
317
318   if (dxgi_format == DXGI_FORMAT_B8G8R8A8_UNORM ||
319       dxgi_format == DXGI_FORMAT_B8G8R8X8_UNORM)
320      mode = D3D12_SURFACE_CONVERSION_BGRA_UINT;
321   else
322      mode = D3D12_SURFACE_CONVERSION_RGBA_UINT;
323
324   if (mode == D3D12_SURFACE_CONVERSION_BGRA_UINT) {
325      if (!surface->rgba_texture) {
326         struct pipe_resource templ = {};
327         struct pipe_resource *src = surface->base.texture;
328
329         templ.format = PIPE_FORMAT_R8G8B8A8_UNORM;
330         templ.width0 = src->width0;
331         templ.height0 = src->height0;
332         templ.depth0 = src->depth0;
333         templ.array_size = src->array_size;
334         templ.nr_samples = src->nr_samples;
335         templ.nr_storage_samples = src->nr_storage_samples;
336         templ.usage = PIPE_USAGE_DEFAULT | PIPE_USAGE_STAGING;
337         templ.bind = src->bind;
338         templ.target = src->target;
339
340         surface->rgba_texture = screen->base.resource_create(&screen->base, &templ);
341      }
342
343      blit_surface(pctx, surface, true);
344      res = d3d12_resource(surface->rgba_texture);
345   }
346
347   if (!d3d12_descriptor_handle_is_allocated(&surface->uint_rtv_handle)) {
348      initialize_rtv(surface->base.context, &res->base.b, &surface->base,
349                     &surface->uint_rtv_handle, DXGI_FORMAT_R8G8B8A8_UINT);
350   }
351
352   return mode;
353}
354
355void
356d3d12_surface_update_post_draw(struct pipe_context *pctx,
357                               struct d3d12_surface *surface,
358                               enum d3d12_surface_conversion_mode mode)
359{
360   if (mode == D3D12_SURFACE_CONVERSION_BGRA_UINT)
361      blit_surface(pctx, surface, false);
362}
363
364D3D12_CPU_DESCRIPTOR_HANDLE
365d3d12_surface_get_handle(struct d3d12_surface *surface,
366                         enum d3d12_surface_conversion_mode mode)
367{
368   if (mode != D3D12_SURFACE_CONVERSION_NONE)
369      return surface->uint_rtv_handle.cpu_handle;
370   return surface->desc_handle.cpu_handle;
371}
372
373void
374d3d12_context_surface_init(struct pipe_context *context)
375{
376   context->create_surface = d3d12_create_surface;
377   context->surface_destroy = d3d12_surface_destroy;
378}
379