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_wgl_public.h"
25 
26 #include <new>
27 
28 #include <windows.h>
29 #include <dxgi1_4.h>
30 #include <directx/d3d12.h>
31 #include <wrl.h>
32 #include <dxguids/dxguids.h>
33 
34 #include "util/u_memory.h"
35 #include "util/u_inlines.h"
36 #include "frontend/api.h"
37 #include "frontend/winsys_handle.h"
38 
39 #include "stw_device.h"
40 #include "stw_pixelformat.h"
41 #include "stw_winsys.h"
42 
43 #include "d3d12/d3d12_format.h"
44 #include "d3d12/d3d12_resource.h"
45 #include "d3d12/d3d12_screen.h"
46 
47 using Microsoft::WRL::ComPtr;
48 constexpr uint32_t num_buffers = 2;
49 
50 struct d3d12_wgl_framebuffer {
51    struct stw_winsys_framebuffer base;
52 
53    struct d3d12_screen *screen;
54    enum pipe_format pformat;
55    HWND window;
56    ComPtr<IDXGISwapChain3> swapchain;
57    struct pipe_resource *buffers[num_buffers];
58 };
59 
60 static struct d3d12_wgl_framebuffer *
d3d12_wgl_framebuffer(struct stw_winsys_framebuffer *fb)61 d3d12_wgl_framebuffer(struct stw_winsys_framebuffer *fb)
62 {
63    return (struct d3d12_wgl_framebuffer *)fb;
64 }
65 
66 static void
d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer *fb, pipe_context *ctx)67 d3d12_wgl_framebuffer_destroy(struct stw_winsys_framebuffer *fb,
68                               pipe_context *ctx)
69 {
70    struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
71    struct pipe_fence_handle *fence = NULL;
72 
73    if (ctx) {
74       /* Ensure all resources are flushed */
75       ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
76       if (fence) {
77          ctx->screen->fence_finish(ctx->screen, ctx, fence, PIPE_TIMEOUT_INFINITE);
78          ctx->screen->fence_reference(ctx->screen, &fence, NULL);
79       }
80    }
81 
82    for (int i = 0; i < num_buffers; ++i) {
83       if (framebuffer->buffers[i]) {
84          d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
85          pipe_resource_reference(&framebuffer->buffers[i], NULL);
86       }
87    }
88 
89    delete framebuffer;
90 }
91 
92 static void
d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer *fb, pipe_context *ctx, pipe_resource *templ)93 d3d12_wgl_framebuffer_resize(stw_winsys_framebuffer *fb,
94                              pipe_context *ctx,
95                              pipe_resource *templ)
96 {
97    struct d3d12_wgl_framebuffer *framebuffer = d3d12_wgl_framebuffer(fb);
98    struct d3d12_dxgi_screen *screen = d3d12_dxgi_screen(framebuffer->screen);
99 
100    DXGI_SWAP_CHAIN_DESC1 desc = {};
101    desc.BufferCount = num_buffers;
102    desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
103    desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
104    desc.Format = d3d12_get_format(templ->format);
105    desc.Width = templ->width0;
106    desc.Height = templ->height0;
107    desc.SampleDesc.Count = 1;
108    desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
109 
110    framebuffer->pformat = templ->format;
111 
112    if (!framebuffer->swapchain) {
113       ComPtr<IDXGISwapChain1> swapchain1;
114       if (FAILED(screen->factory->CreateSwapChainForHwnd(
115          screen->base.cmdqueue,
116          framebuffer->window,
117          &desc,
118          nullptr,
119          nullptr,
120          &swapchain1))) {
121          debug_printf("D3D12: failed to create swapchain");
122          return;
123       }
124 
125       swapchain1.As(&framebuffer->swapchain);
126 
127       screen->factory->MakeWindowAssociation(framebuffer->window,
128                                              DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN);
129    }
130    else {
131       struct pipe_fence_handle *fence = NULL;
132 
133       /* Ensure all resources are flushed */
134       ctx->flush(ctx, &fence, PIPE_FLUSH_HINT_FINISH);
135       if (fence) {
136          ctx->screen->fence_finish(ctx->screen, ctx, fence, PIPE_TIMEOUT_INFINITE);
137          ctx->screen->fence_reference(ctx->screen, &fence, NULL);
138       }
139 
140       for (int i = 0; i < num_buffers; ++i) {
141          if (framebuffer->buffers[i]) {
142             d3d12_resource_release(d3d12_resource(framebuffer->buffers[i]));
143             pipe_resource_reference(&framebuffer->buffers[i], NULL);
144          }
145       }
146       if (FAILED(framebuffer->swapchain->ResizeBuffers(num_buffers, desc.Width, desc.Height, desc.Format, desc.Flags))) {
147          debug_printf("D3D12: failed to resize swapchain");
148       }
149    }
150 }
151 
152 static boolean
d3d12_wgl_framebuffer_present(stw_winsys_framebuffer *fb)153 d3d12_wgl_framebuffer_present(stw_winsys_framebuffer *fb)
154 {
155    auto framebuffer = d3d12_wgl_framebuffer(fb);
156    if (!framebuffer->swapchain) {
157       debug_printf("D3D12: Cannot present; no swapchain");
158       return false;
159    }
160 
161    if (stw_dev->swap_interval < 1)
162       return S_OK == framebuffer->swapchain->Present(0, DXGI_PRESENT_ALLOW_TEARING);
163    else
164        return S_OK == framebuffer->swapchain->Present(stw_dev->swap_interval, 0);
165 }
166 
167 static struct pipe_resource *
d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer *pframebuffer, st_attachment_type statt)168 d3d12_wgl_framebuffer_get_resource(struct stw_winsys_framebuffer *pframebuffer,
169                                    st_attachment_type statt)
170 {
171    auto framebuffer = d3d12_wgl_framebuffer(pframebuffer);
172    auto pscreen = &framebuffer->screen->base;
173 
174    if (!framebuffer->swapchain)
175       return nullptr;
176 
177    UINT index = framebuffer->swapchain->GetCurrentBackBufferIndex();
178    if (statt == ST_ATTACHMENT_FRONT_LEFT)
179       index = !index;
180 
181    if (framebuffer->buffers[index]) {
182       pipe_reference(NULL, &framebuffer->buffers[index]->reference);
183       return framebuffer->buffers[index];
184    }
185 
186    ID3D12Resource *res;
187    framebuffer->swapchain->GetBuffer(index, IID_PPV_ARGS(&res));
188    if (!res)
189       return nullptr;
190 
191    struct winsys_handle handle;
192    memset(&handle, 0, sizeof(handle));
193    handle.type = WINSYS_HANDLE_TYPE_D3D12_RES;
194    handle.format = framebuffer->pformat;
195    handle.com_obj = res;
196 
197    D3D12_RESOURCE_DESC res_desc = GetDesc(res);
198 
199    struct pipe_resource templ;
200    memset(&templ, 0, sizeof(templ));
201    templ.target = PIPE_TEXTURE_2D;
202    templ.format = framebuffer->pformat;
203    templ.width0 = res_desc.Width;
204    templ.height0 = res_desc.Height;
205    templ.depth0 = 1;
206    templ.array_size = res_desc.DepthOrArraySize;
207    templ.nr_samples = res_desc.SampleDesc.Count;
208    templ.last_level = res_desc.MipLevels - 1;
209    templ.bind = PIPE_BIND_DISPLAY_TARGET | PIPE_BIND_RENDER_TARGET;
210    templ.usage = PIPE_USAGE_DEFAULT;
211    templ.flags = 0;
212 
213    pipe_resource_reference(&framebuffer->buffers[index],
214                            pscreen->resource_from_handle(pscreen, &templ, &handle,
215                                                          PIPE_HANDLE_USAGE_FRAMEBUFFER_WRITE));
216    return framebuffer->buffers[index];
217 }
218 
219 struct stw_winsys_framebuffer *
d3d12_wgl_create_framebuffer(struct pipe_screen *screen, HWND hWnd, int iPixelFormat)220 d3d12_wgl_create_framebuffer(struct pipe_screen *screen,
221                              HWND hWnd,
222                              int iPixelFormat)
223 {
224    const struct stw_pixelformat_info *pfi =
225       stw_pixelformat_get_info(iPixelFormat);
226    if (!(pfi->pfd.dwFlags & PFD_DOUBLEBUFFER) ||
227        (pfi->pfd.dwFlags & PFD_SUPPORT_GDI))
228       return NULL;
229 
230    struct d3d12_wgl_framebuffer *fb = CALLOC_STRUCT(d3d12_wgl_framebuffer);
231    if (!fb)
232       return NULL;
233 
234    new (fb) struct d3d12_wgl_framebuffer();
235 
236    fb->window = hWnd;
237    fb->screen = d3d12_screen(screen);
238    fb->base.destroy = d3d12_wgl_framebuffer_destroy;
239    fb->base.resize = d3d12_wgl_framebuffer_resize;
240    fb->base.present = d3d12_wgl_framebuffer_present;
241    fb->base.get_resource = d3d12_wgl_framebuffer_get_resource;
242 
243    return &fb->base;
244 }
245